In my application I'm performing somewhat heavy lookup operations. These operations must be done within a single thread (persistence framework limitation).
I want to cache the results. Thus, I have a class UMRCache, with an inner class Worker:
public class UMRCache {
private Worker worker;
private List<String> requests = Collections.synchronizedList<new ArrayList<String>>());
private Map<String, Object> cache = Collections.synchronizedMap(new HashMap<String, Object>());
public UMRCache(Repository repository) {
this.worker = new Worker(repository);
this.worker.start();
}
public Object get(String key) {
if (this.cache.containsKey(key)) {
// If the element is already cached, get value from cache
return this.cache.get(key);
}
synchronized (this.requests) {
// Add request to queue
this.requests.add(key);
// Notify the Worker thread that there's work to do
this.requests.notifyAll();
}
synchronized (this.cache) {
// Wait until Worker has updated the cache
this.cache.wait();
// Now, cache should contain a value for key
return this.cache.get(key);
}
}
private class Worker extends Thread {
public void run() {
boolean doRun = true;
while (doRun) {
synchronized (requests) {
while (requests.isEmpty() && doRun) {
requests.wait(); // Wait until there's work to do
}
synchronized (cache) {
Set<String> processed = new HashSet<String>();
for (String key : requests) {
// Do the lookup
Object result = respository.lookup(key);
// Save to cache
cache.put(key, result);
processed.add(key);
}
// Remove processed requests from queue
requests.removeAll(processed);
// Notify all threads waiting for their requests to be served
cache.notifyAll();
}
}
}
}
}
}
I have a testcase for this:
public class UMRCacheTest extends TestCase {
private UMRCache umrCache;
public void setUp() throws Exception {
super.setUp();
umrCache = new UMRCache(repository);
}
public void testGet() throws Exception {
for (int i = 0; i < 10000; i++) {
final List fetched = Collections.synchronizedList(new ArrayList());
final String[] keys = new String[]{"key1", "key2"};
final String[] expected = new String[]{"result1", "result2"}
final Random random = new Random();
Runnable run1 = new Runnable() {
public void run() {
for (int i = 0; i < keys.length; i++) {
final String key = keys[i];
final Object result = umrCache.get(key);
assertEquals(key, results[i]);
fetched.add(um);
try {
Thread.sleep(random.nextInt(3));
} catch (InterruptedException ignore) {
}
}
}
};
Runnable run2 = new Runnable() {
public void run() {
for (int i = keys.length - 1; i >= 0; i--) {
final String key = keys[i];
final String result = umrCache.get(key);
assertEquals(key, results[i]);
fetched.add(um);
try {
Thread.sleep(random.nextInt(3));
} catch (InterruptedException ignore) {
}
}
}
};
final Thread thread1 = new Thread(run1);
thread1.start();
final Thread thread2 = new Thread(run2);
thread2.start();
final Thread thread3 = new Thread(run1);
thread3.start();
thread1.join();
thread2.join();
thread3.join();
umrCache.dispose();
assertEquals(6, fetched.size());
}
}
}
The test fails randomly, at about 1 out of 10 runs. It will fail at the last assertion: assertEquals(6, fetched.size()), at assertEquals(key, results[i]), or sometimes the test runner will never finish.
So there's something buggy about my thread logic. Any tips?
EDIT:
I might have cracked it now, thanks to all who have helped.
The solution seems to be:
public Object get(String key) {
if (this.cache.containsKey(key)) {
// If the element is already cached, get value from cache
return this.cache.get(key);
}
synchronized (this.requests) {
// Add request to queue
this.requests.add(key);
// Notify the Worker thread that there's work to do
this.requests.notifyAll();
}
synchronized (this.cache) {
// Wait until Worker has updated the cache
while (!this.cache.containsKey(key)) {
this.cache.wait();
}
// Now, cache should contain a value for key
return this.cache.get(key);
}
}
get() method logic can miss result and get stuck
synchronized (this.requests) {
// Add request to queue
this.requests.add(key);
// Notify the Worker thread that there's work to do
this.requests.notifyAll();
}
// ----- MOMENT1. If at this moment Worker puts result into cache it
// will be missed since notification will be lost
synchronized (this.cache) {
// Wait until Worker has updated the cache
this.cache.wait();
// ----- MOMENT2. May be too late, since cache notifiation happened before at MOMENT1
// Now, cache should contain a value for key
return this.cache.get(key);
}
The variable fetched in your test is an ArrayList and is accessed and updated from your two anonymous Runnable instances.
ArrayList is not thread safe, from the documentation:
Note that this implementation is not
synchronized. If multiple threads
access an ArrayList instance
concurrently, and at least one of the
threads modifies the list
structurally, it must be synchronized
externally. (A structural modification
is any operation that adds or deletes
one or more elements, or explicitly
resizes the backing array; merely
setting the value of an element is not
a structural modification.) This is
typically accomplished by
synchronizing on some object that
naturally encapsulates the list. If no
such object exists, the list should be
"wrapped" using the
Collections.synchronizedList method.
This is best done at creation time, to
prevent accidental unsynchronized
access to the list:
Hence I think your test needs a little adjusting.
I noticed your lookup in cache isn't atomic operation:
if (this.cache.containsKey(key)) {
// If the element is already cached, get value from cache
return this.cache.get(key);
}
Since you never delete from cache in your code, you always will get some value by this code. But if, in future, you plan to clean cache, lack of atomicity here will become a problem.
Related
I have a class in which I have a ConcurrentHashMap which is updated by a single thread every 30 seconds and then I have multiple reader threads reading from the same ConcurrentHashMap by calling getNextSocket() method.
Below is my singleton class which on the initialization calls connectToSockets() method to populate my ConcurrentHashMap and then starts a background thread which updates the same map every 30 second by calling updateSockets() method.
And then from multiple threads I am calling getNextSocket() method to get next available live socket which uses same map to get the information out. I also have SocketInfo class which is immutable which contains the state of all the sockets whether they are live or not.
public class SocketHolder {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Map<DatacenterEnum, List<SocketInfo>> liveSocketsByDc = new ConcurrentHashMap<>();
// Lazy Loaded Singleton Pattern
private static class Holder {
private static final SocketHolder INSTANCE = new SocketHolder();
}
public static SocketHolder getInstance() {
return Holder.INSTANCE;
}
private SocketHolder() {
connectToSockets();
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
updateSockets();
}
}, 30, 30, TimeUnit.SECONDS);
}
private void connectToSockets() {
Map<DatacenterEnum, ImmutableList<String>> socketsByDc = TestUtils.SERVERS;
for (Map.Entry<DatacenterEnum, ImmutableList<String>> entry : socketsByDc.entrySet()) {
List<SocketInfo> addedColoSockets = connect(entry.getKey(), entry.getValue(), ZMQ.PUSH);
liveSocketsByDc.put(entry.getKey(), addedColoSockets);
}
}
private List<SocketInfo> connect(DatacenterEnum dc, List<String> addresses, int socketType) {
List<SocketInfo> socketList = new ArrayList<>();
// ... some code here
return socketList;
}
// called from multiple reader threads to get next live available socket
public Optional<SocketInfo> getNextSocket() {
Optional<SocketInfo> liveSocket = getLiveSocket(liveSocketsByDc.get(DatacenterEnum.CORP));
return liveSocket;
}
private Optional<SocketInfo> getLiveSocket(final List<SocketInfo> listOfEndPoints) {
if (!CollectionUtils.isEmpty(listOfEndPoints)) {
Collections.shuffle(listOfEndPoints);
for (SocketInfo obj : listOfEndPoints) {
if (obj.isLive()) {
return Optional.of(obj);
}
}
}
return Optional.absent();
}
// update CHM map every 30 seconds
private void updateSockets() {
Map<DatacenterEnum, ImmutableList<String>> socketsByDc = TestUtils.SERVERS;
for (Entry<DatacenterEnum, ImmutableList<String>> entry : socketsByDc.entrySet()) {
List<SocketInfo> liveSockets = liveSocketsByDc.get(entry.getKey());
List<SocketInfo> liveUpdatedSockets = new ArrayList<>();
for (SocketInfo liveSocket : liveSockets) {
Socket socket = liveSocket.getSocket();
String endpoint = liveSocket.getEndpoint();
boolean sent = ....;
boolean isLive = sent ? true : false;
// is this right here? or will it cause any race condition?
SocketInfo state = new SocketInfo(socket, liveSocket.getContext(), endpoint, isLive);
liveUpdatedSockets.add(state);
}
// update map with new liveUpdatedSockets
liveSocketsByDc.put(entry.getKey(), liveUpdatedSockets);
}
}
}
Question:
Is my above code thread safe and there is no race condition in updateSockets() and getNextSocket() method?
In my updateSockets() method, I extract List<SocketInfo> from liveSocketsByDc ConcurrentHashMap which was already populated before in connectToSockets() method during initialization or next interval of 30 second in updateSockets() method and then I am iterating same list liveSockets and create a new SocketInfo object depending on whether isLive is true or false. And then I update liveSocketsByDc ConcurrentHashMap with this new SocketInfo object. Does this look right? Because from multiple reader threads I am going to call getNextSocket() method which inturn calls getLiveSocket method which uses same map to get the next available live socket.
I am iterating liveSockets list and then creating a new SocketInfo object by just changing isLive field and other things will stay same. Is this right?
If there is a thread safety issue, what is the best way to fix this?
Here:
List<SocketInfo> liveSockets = liveSocketsByDc.get(entry.getKey());
Your different threads are potentially writing/reading the same list object in parallel.
So: not thread safe. It doesn't help to have an "outer" thread-safe data structure; when that thread-safe thing contains data ... that is not thread-safe; but then "worked on" in parallel!
I have one producer and many consumers.
the producer is fast and generating a lot of results
tokens with the same value need to be processed sequentially
tokens with different values must be processed in parallel
creating new Runnables would be very expensive and also the production code could work with 100k of Tokens(in order to create a Runnable I have to pass to the constructor some complex to build objects)
Can I achieve the same results with a simpler algorithm? Nesting a syncronization block with a reentrant lock seems a bit unnatural.
Are there any race conditions you might notice?
Update: a second solution I found was working with 3 collections. One to cache the producer results, second a blocking queue and 3rd using a list to track in the tasks in progress. Again a bit to complicated.
My version of code
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
public class Main1 {
static class Token {
private int order;
private String value;
Token() {
}
Token(int o, String v) {
order = o;
value = v;
}
int getOrder() {
return order;
}
String getValue() {
return value;
}
}
private final static BlockingQueue<Token> queue = new ArrayBlockingQueue<Token>(10);
private final static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();
private final static ReentrantLock reentrantLock = new ReentrantLock();
private final static Token STOP_TOKEN = new Token();
private final static List<String> lockList = Collections.synchronizedList(new ArrayList<String>());
public static void main(String[] args) {
ExecutorService producerExecutor = Executors.newSingleThreadExecutor();
producerExecutor.submit(new Runnable() {
public void run() {
Random random = new Random();
try {
for (int i = 1; i <= 100; i++) {
Token token = new Token(i, String.valueOf(random.nextInt(1)));
queue.put(token);
}
queue.put(STOP_TOKEN);
}catch(InterruptedException e){
e.printStackTrace();
}
}
});
ExecutorService consumerExecutor = Executors.newFixedThreadPool(10);
for(int i=1; i<=10;i++) {
// creating to many runnable would be inefficient because of this complex not thread safe object
final Object dependecy = new Object(); //new ComplexDependecy()
consumerExecutor.submit(new Runnable() {
public void run() {
while(true) {
try {
//not in order
Token token = queue.take();
if (token == STOP_TOKEN) {
queue.add(STOP_TOKEN);
return;
}
System.out.println("Task start" + Thread.currentThread().getId() + " order " + token.getOrder());
Random random = new Random();
Thread.sleep(random.nextInt(200)); //doLongRunningTask(dependecy)
lockList.remove(token.getValue());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}});
}
}}
You can pre-create set of Runnables which will pick incoming tasks (tokens) and place them in queues according to their order value.
As pointed out in comments, it's not guaranteed that tokens with different values will always execute in parallel (all in all, you are bounded, at least, by nr of physical cores in your box). However, it is guaranteed that tokens with same order will be executed in the order of arrival.
Sample code:
/**
* Executor which ensures incoming tasks are executed in queues according to provided key (see {#link Task#getOrder()}).
*/
public class TasksOrderingExecutor {
public interface Task extends Runnable {
/**
* #return ordering value which will be used to sequence tasks with the same value.<br>
* Tasks with different ordering values <i>may</i> be executed in parallel, but not guaranteed to.
*/
String getOrder();
}
private static class Worker implements Runnable {
private final LinkedBlockingQueue<Task> tasks = new LinkedBlockingQueue<>();
private volatile boolean stopped;
void schedule(Task task) {
tasks.add(task);
}
void stop() {
stopped = true;
}
#Override
public void run() {
while (!stopped) {
try {
Task task = tasks.take();
task.run();
} catch (InterruptedException ie) {
// perhaps, handle somehow
}
}
}
}
private final Worker[] workers;
private final ExecutorService executorService;
/**
* #param queuesNr nr of concurrent task queues
*/
public TasksOrderingExecutor(int queuesNr) {
Preconditions.checkArgument(queuesNr >= 1, "queuesNr >= 1");
executorService = new ThreadPoolExecutor(queuesNr, queuesNr, 0, TimeUnit.SECONDS, new SynchronousQueue<>());
workers = new Worker[queuesNr];
for (int i = 0; i < queuesNr; i++) {
Worker worker = new Worker();
executorService.submit(worker);
workers[i] = worker;
}
}
public void submit(Task task) {
Worker worker = getWorker(task);
worker.schedule(task);
}
public void stop() {
for (Worker w : workers) w.stop();
executorService.shutdown();
}
private Worker getWorker(Task task) {
return workers[task.getOrder().hashCode() % workers.length];
}
}
By the nature of your code, the only way to guarantee that the tokens with the
same value are processed in serial manner is to wait for STOP_TOKEN to arrive.
You'll need single producer-single consumer setup, with consumer collecting and sorting
the tokens by their value (into the Multimap, let say).
Only then you know which tokens can be process serially and which may be processed in parallel.
Anyway, I advise you to look at LMAX Disruptor, which offers very effective way for sharing data between threads.
It doesn't suffer from synchronization overhead as Executors as it is lock free (which may give you nice performance benefits, depending on the way how you process the data).
The solution using two Disruptors
// single thread for processing as there will be only on consumer
Disruptor<InEvent> inboundDisruptor = new Disruptor<>(InEvent::new, 32, Executors.newSingleThreadExecutor());
// outbound disruptor that uses 3 threads for event processing
Disruptor<OutEvent> outboundDisruptor = new Disruptor<>(OutEvent::new, 32, Executors.newFixedThreadPool(3));
inboundDisruptor.handleEventsWith(new InEventHandler(outboundDisruptor));
// setup 3 event handlers, doing round robin consuming, effectively processing OutEvents in 3 threads
outboundDisruptor.handleEventsWith(new OutEventHandler(0, 3, new Object()));
outboundDisruptor.handleEventsWith(new OutEventHandler(1, 3, new Object()));
outboundDisruptor.handleEventsWith(new OutEventHandler(2, 3, new Object()));
inboundDisruptor.start();
outboundDisruptor.start();
// publisher code
for (int i = 0; i < 10; i++) {
inboundDisruptor.publishEvent(InEventTranslator.INSTANCE, new Token());
}
The event handler on the inbound disruptor just collects incoming tokens. When STOP token is received, it publishes the series of tokens to outbound disruptor for further processing:
public class InEventHandler implements EventHandler<InEvent> {
private ListMultimap<String, Token> tokensByValue = ArrayListMultimap.create();
private Disruptor<OutEvent> outboundDisruptor;
public InEventHandler(Disruptor<OutEvent> outboundDisruptor) {
this.outboundDisruptor = outboundDisruptor;
}
#Override
public void onEvent(InEvent event, long sequence, boolean endOfBatch) throws Exception {
if (event.token == STOP_TOKEN) {
// publish indexed tokens to outbound disruptor for parallel processing
tokensByValue.asMap().entrySet().stream().forEach(entry -> outboundDisruptor.publishEvent(OutEventTranslator.INSTANCE, entry.getValue()));
} else {
tokensByValue.put(event.token.value, event.token);
}
}
}
Outbound event handler processes tokens of the same value sequentially:
public class OutEventHandler implements EventHandler<OutEvent> {
private final long order;
private final long allHandlersCount;
private Object yourComplexDependency;
public OutEventHandler(long order, long allHandlersCount, Object yourComplexDependency) {
this.order = order;
this.allHandlersCount = allHandlersCount;
this.yourComplexDependency = yourComplexDependency;
}
#Override
public void onEvent(OutEvent event, long sequence, boolean endOfBatch) throws Exception {
if (sequence % allHandlersCount != order ) {
// round robin, do not consume every event to allow parallel processing
return;
}
for (Token token : event.tokensToProcessSerially) {
// do procesing of the token using your complex class
}
}
}
The rest of the required infrastructure (purpose described in the Disruptor docs):
public class InEventTranslator implements EventTranslatorOneArg<InEvent, Token> {
public static final InEventTranslator INSTANCE = new InEventTranslator();
#Override
public void translateTo(InEvent event, long sequence, Token arg0) {
event.token = arg0;
}
}
public class OutEventTranslator implements EventTranslatorOneArg<OutEvent, Collection<Token>> {
public static final OutEventTranslator INSTANCE = new OutEventTranslator();
#Override
public void translateTo(OutEvent event, long sequence, Collection<Token> tokens) {
event.tokensToProcessSerially = tokens;
}
}
public class InEvent {
// Note that no synchronization is used here,
// even though the field is used among multiple threads.
// Memory barrier used by Disruptor guarantee changes are visible.
public Token token;
}
public class OutEvent {
// ... again, no locks.
public Collection<Token> tokensToProcessSerially;
}
public class Token {
String value;
}
If you have lots of different tokens, then the simplest solution is to create some number of single-thread executors (about 2x your number of cores), and then distribute each task to an executor determined by the hash of its token.
That way all tasks with the same token will go to the same executor and execute sequentially, because each executor only has one thread.
If you have some unstated requirements about scheduling fairness, then it is easy enough to avoid any significant imbalances by having the producer thread queue up its requests (or block) before distributing them, until there are, say, less than 10 requests per executor outstanding.
The following solution will only use a single Map that is used by the producer and consumers to process orders in sequential order for each order number while processing different order numbers in parallel. Here is the code:
public class Main {
private static final int NUMBER_OF_CONSUMER_THREADS = 10;
private static volatile int sync = 0;
public static void main(String[] args) {
final ConcurrentHashMap<String,Controller> queues = new ConcurrentHashMap<String, Controller>();
final CountDownLatch latch = new CountDownLatch(NUMBER_OF_CONSUMER_THREADS);
final AtomicBoolean done = new AtomicBoolean(false);
// Create a Producer
new Thread() {
{
this.setDaemon(true);
this.setName("Producer");
this.start();
}
public void run() {
Random rand = new Random();
for(int i =0 ; i < 1000 ; i++) {
int order = rand.nextInt(20);
String key = String.valueOf(order);
String value = String.valueOf(rand.nextInt());
Controller controller = queues.get(key);
if (controller == null) {
controller = new Controller();
queues.put(key, controller);
}
controller.add(new Token(order, value));
Main.sync++;
}
done.set(true);
}
};
while (queues.size() < 10) {
try {
// Allow the producer to generate several entries that need to
// be processed.
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
// System.out.println(queues);
// Create the Consumers
ExecutorService consumers = Executors.newFixedThreadPool(NUMBER_OF_CONSUMER_THREADS);
for(int i = 0 ; i < NUMBER_OF_CONSUMER_THREADS ; i++) {
consumers.submit(new Runnable() {
private Random rand = new Random();
public void run() {
String name = Thread.currentThread().getName();
try {
boolean one_last_time = false;
while (true) {
for (Map.Entry<String, Controller> entry : queues.entrySet()) {
Controller controller = entry.getValue();
if (controller.lock(this)) {
ConcurrentLinkedQueue<Token> list = controller.getList();
Token token;
while ((token = list.poll()) != null) {
try {
System.out.println(name + " processing order: " + token.getOrder()
+ " value: " + token.getValue());
Thread.sleep(rand.nextInt(200));
} catch (InterruptedException e) {
}
}
int last = Main.sync;
queues.remove(entry.getKey());
while(done.get() == false && last == Main.sync) {
// yield until the producer has added at least another entry
Thread.yield();
}
// Purge any new entries added
while ((token = list.poll()) != null) {
try {
System.out.println(name + " processing order: " + token.getOrder()
+ " value: " + token.getValue());
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
controller.unlock(this);
}
}
if (one_last_time) {
return;
}
if (done.get()) {
one_last_time = true;
}
}
} finally {
latch.countDown();
}
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
consumers.shutdown();
System.out.println("Exiting.. remaining number of entries: " + queues.size());
}
}
Note that the Main class contains a queues instance that is a Map. The map key is the order id that you want to process sequentially by the consumers. The value is a Controller class that will contain all of the orders associated with that order id.
The producer will generate the orders and add the order, (Token), to its associated Controller. The consumers will iterator over the queues map values and call the Controller lock method to determine if it can process orders for that particular order id. If the lock returns false it will check the next Controller instance. If the lock returns true, it will process all orders and then check the next Controller.
updated Added the sync integer that is used to guarantee that when an instance of the Controller is removed from the queues map. All of its entries will be consumed. There was an logic error in the consumer code where the unlock method was called to soon.
The Token class is similar to the one that you've posted here.
class Token {
private int order;
private String value;
Token(int order, String value) {
this.order = order;
this.value = value;
}
int getOrder() {
return order;
}
String getValue() {
return value;
}
#Override
public String toString() {
return "Token [order=" + order + ", value=" + value + "]\n";
}
}
The Controller class that follows is used to insure that only a single thread within the thread pool will be processing the orders. The lock/unlock methods are used to determine which of the threads will be allowed to process the orders.
class Controller {
private ConcurrentLinkedQueue<Token> tokens = new ConcurrentLinkedQueue<Token>();
private ReentrantLock lock = new ReentrantLock();
private Runnable current = null;
void add(Token token) {
tokens.add(token);
}
public ConcurrentLinkedQueue<Token> getList() {
return tokens;
}
public void unlock(Runnable runnable) {
lock.lock();
try {
if (current == runnable) {
current = null;
}
} finally {
lock.unlock();
}
}
public boolean lock(Runnable runnable) {
lock.lock();
try {
if (current == null) {
current = runnable;
}
} finally {
lock.unlock();
}
return current == runnable;
}
#Override
public String toString() {
return "Controller [tokens=" + tokens + "]";
}
}
Additional information about the implementation. It uses a CountDownLatch to insure that all produced orders will be processed prior to the process exiting. The done variable is just like your STOP_TOKEN variable.
The implementation does contain an issue that you would need to resolve. There is the issue that it does not purge the controller for an order id when all of the orders have been processed. This will cause instances where a thread in the thread pool gets assigned to a controller that contains no orders. Which will waste cpu cycles that could be used to perform other tasks.
Is all you need is to ensure that tokens with the same value are not being processed concurrently? Your code is too messy to understand what you mean (it does not compile, and has lots of unused variables, locks and maps, that are created but never used). It looks like you are greatly overthinking this. All you need is one queue, and one map.
Something like this I imagine:
class Consumer implements Runnable {
ConcurrentHashMap<String, Token> inProcess;
BlockingQueue<Token> queue;
public void run() {
Token token = null;
while ((token = queue.take()) != null) {
if(inProcess.putIfAbsent(token.getValue(), token) != null) {
queue.put(token);
continue;
}
processToken(token);
inProcess.remove(token.getValue());
}
}
}
tokens with the same value need to be processed sequentially
The way to insure that any two things happen in sequence is to do them in the same thread.
I'd have a collection of however many worker threads, and I'd have a Map. Any time I get a token that I've not seen before, I'll pick a thread at random, and enter the token and the thread into the map. From then on, I'll use that same thread to execute tasks associated with that token.
creating new Runnables would be very expensive
Runnable is an interface. Creating new objects that implement Runnable is not going to be significantly more expensive than creating any other kind of object.
Maybe I'm misunderstanding something. But it seems that it would be easier to filter the Tokens with same value from the ones with different values into two different queues initially.
And then use Stream with either map or foreach for the sequential. And simply use the parallel stream version for the rest.
If your Tokens in production environment are lazily generated and you only get one at a time you simply make some sort of filter which distributes them to the two different queues.
If you can implement it with Streams I suqqest doing that as they are simple, easy to use and FAST!
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
I made a brief example of what I mean. In this case the numbers Tokens are sort of artificially constructed but thats beside the point. Also the streams are both initiated on the main thread which would probably also not be ideal.
public static void main(String args[]) {
ArrayList<Token> sameValues = new ArrayList<Token>();
ArrayList<Token> distinctValues = new ArrayList<Token>();
Random random = new Random();
for (int i = 0; i < 100; i++) {
int next = random.nextInt(100);
Token n = new Token(i, String.valueOf(next));
if (next == i) {
sameValues.add(n);
} else {
distinctValues.add(n);
}
}
distinctValues.stream().parallel().forEach(token -> System.out.println("Distinct: " + token.value));
sameValues.stream().forEach(token -> System.out.println("Same: " + token.value));
}
I am not entirely sure I have understood the question but I'll take a stab at an algorithm.
The actors are:
A queue of tasks
A pool of free executors
A set of in-process tokens currently being processed
A controller
Then,
Initially all executors are available and the set is empty
controller picks an available executor and goes through the queue looking for a task with a token that is not in the in-process set and when it finds it
adds the token to the in-process set
assigns the executor to process the task and
goes back to the beginning of the queue
the executor removes the token from the set when it is done processing and adds itself back to the pool
One way of doing this is having one executor for sequence processing and one for parallel processing. We also need a single threaded manager service that will decide to which service token needs to be submitted for processing.
// Queue to be shared by both the threads. Contains the tokens produced by producer.
BlockingQueue tokenList = new ArrayBlockingQueue(10);
private void startProcess() {
ExecutorService producer = Executors.newSingleThreadExecutor();
final ExecutorService consumerForSequence = Executors
.newSingleThreadExecutor();
final ExecutorService consumerForParallel = Executors.newFixedThreadPool(10);
ExecutorService manager = Executors.newSingleThreadExecutor();
producer.submit(new Producer(tokenList));
manager.submit(new Runnable() {
public void run() {
try {
while (true) {
Token t = tokenList.take();
System.out.println("consumed- " + t.orderid
+ " element");
if (t.orderid % 7 == 0) { // any condition to check for sequence processing
consumerForSequence.submit(new ConsumerForSequenceProcess(t));
} else {
ConsumerForParallel.submit(new ConsumerForParallelProcess(t));
}
}
}
catch (InterruptedException e) { // TODO Auto-generated catch
// block
e.printStackTrace();
}
}
});
}
I think there is a more fundamental design issue hidden behind this task, but ok. I cant figure out from you problem description if you want in-order execution or if you just want operations on tasks described by single tokens to be atomic/transactional. What i propose below feels more like a "quick fix" to this issue than a real solution.
For the real "ordered execution" case I propose a solution which is based on queue proxies which order the output:
Define a implementation of Queue which provides a factory method generating proxy queues which are represented to the producer side by a this single queue object; the factory method should also register these proxy queue objects. adding an element to the input queue should add it directly to one of the output queues if it matches one of the elements in one of the output queues. Otherwise add it to any (the shortest) output queue. (implement the check for this efficiently). Alternatively (slightly better): don't do this when the element is added, but when any of the output queues runs empty.
Give each of your runnable consumers an field storing an individual Queue interface (instead of accessing a single object). Initialize this field by a the factory method defined above.
For the transaction case i think it's easier to span more threads than you have cores (use statistics to calculate this), and implement the blocking mechanism on an lower (object) level.
I have the following situation: I'm concurrently processing requests that have a given key. I can process any number of requests at the same time, as long as each key in progress is unique.
I am a total rookie with concurrency in Java. There must be some pattern/utility/existing question for this, but I can't figure out what to search for. Hoping somebody could point me in the right direction, or comment on what I have so far.
This class manages the locks:
class LockMap<K> {
private Map<K, Object> locks = new HashMap<>();
void acquireLock(K key) throws InterruptedException {
Object lockObj;
synchronized (locks) {
lockObj = locks.get(key);
if (lockObj == null) lockObj = new Object();
locks.put(key, lockObj);
}
synchronized (lockObj) {
lockObj.wait();
}
}
void releaseLock(K key) {
Object lockObj;
synchronized (locks) {
lockObj = locks.get(key);
locks.remove(key);
}
if (lockObj != null) {
synchronized (lockObj) {
lockObj.notify();
}
}
}
}
Then I use the lock manager like this:
// lockMap is instance of LockMap shared across all threads
void doSomething(K key) {
lockMap.acquireLock(key);
try {
// something
} finally {
lockMap.releaseLock(key);
}
}
Is this the right way to do it?
How about this:
create a ConcurrentHashMap<K,Semaphore>
ConcurrentMap<K, Semaphore> myMap = new ConcurrentHashMap<>();
in your doSomething() method, use the putIfAbsent() method to of your map to add a semaphore with one permit to the map, only if the key does not exist in the map.
subsequently do a get() on the key to fetch the semaphore for that key, and then do your stuff. Release the semaphore when done.
void doSomething(K key) {
myMap.putIfAbsent(key, new Semaphore(1));
Semaphore s = myMap.get(myKey);
s.aquire();
try {
// do stuff
} finally {
s.release();
}
}
The only real problem with this scheme is if your list of keys will grow indefinitely, I don't have a good race-condition-free strategy for removing the semaphore from the map. (But if you know you will reuse the same keys over and over, or the list will grow slowly, then maybe this is ok.)
Following solution does not locks LockMap and so is extremly parallel. It uses custom-made Locks to track the moment when they can be deleted, and handles concurrent deletion/creation.
class Lock {
boolean busy=true; // locked state, a thread is working
int waitCount=0; // number of waiting threads
/** returns true if lock succeeded */
synchronized boolean tryLock() throws InterruptedException {
if (busy) {
waitCount++;
} else if (waitCount==0){
// such values mean that the lock is deleted
return false;
} else {
busy=true;
return true;
}
for (;;) {
wait();
if (!busy) {
waitCount--;
busy=true;
return true;
}
}
}
}
class LockMap<K> {
private ConcurrentHashMap<K, Lock> locks = new ConcurrentHashMap<>();
void acquireLock(K key) throws InterruptedException {
for (;;) {
Lock lockObj = locks.get(key);
if (lockObj==null) {
Lock myLockObj = new Lock();
lockObj=locks.putIfAbsent(key, myLockObj);
if (lockObj==null) {
// successfully inserted, and so locked
return;
}
}
// lockObj existed, lock it or wait in queue
if (lockObj.tryLock()) {
return;
}
}
}
void releaseLock(K key) {
Lock lockObj = locks.get(key);
synchronized (lockObj) {
lockObj.busy=false;
if (lockObj.waitCount==0) {
locks.remove(key);
} else {
lockObj.notify();
}
}
}
}
I've been programming in Java for sometime but new to concurrent programming, so bear with me!
I'm trying to develop a class that holds a group of Collection classes [eg ArrayLists] and then to find a specified value it traverses all collections at the same time, stopping all threads if it finds the given value.
I've pasted my code below and was wondering if anyone knows how within contains_multiple_collections() I catch if one of the threads returned Futures has returned true?
Thanks
Graham
public class CollectionGroup<V> extends ContainerGroup
{
//...
public boolean contains(V value)
{
boolean containsValue = false;
if (mCollections.size() == 1)
{
containsValue = mCollections.get(0).contains(value);
}
else
{
containsValue = contains_multiple_collections(value);
}
return containsValue;
}
private boolean contains_multiple_collections(V value)
{
// thread pool
int numberProcessors = mCollections.size();
ExecutorService es = Executors.newFixedThreadPool(numberProcessors);
for (int i=0; i<numberProcessors; i++)
{
AbstractCollection<V> collection = mCollections.get(i);
MyCallable callable = new MyCallable(collection,value);
Future<Boolean> future = es.submit(callable);
//...
}
return true;
}
private class MyCallable implements Callable<Boolean>
{
protected AbstractCollection<V> mCollection;
protected V mValue;
public MyCallable(AbstractCollection<V> collection, V value)
{
mCollection = collection;
mValue = value;
}
#Override
public Boolean call() throws Exception
{
boolean ok = mCollection.contains(mValue);
return ok;
}
} // class MyCallable
} // class CollectionGroup
contains won't stop just because you might have found the value in another thread. The only way to do this is to loop yourself.
For a CPU intensive process, the optimal number of threads is likely to be the number of cores you have. Creating too many threads adds overhead which slows down your solution.
You should also remember to shutdown() the ExecutorService when you are finished with it.
If you want to speed up the search, I would maintain a Set of all values this is likely to be 10-100x faster than using multiple threads.
You could use an ExecutorCompletionService. Just keep take()ing (take() blocks until a completed Future is present) until you get a result that is true and shutdownNow() the underlying ExecturService once you've found something. This won't immediately stop all threads once a value is found though.
The issue is that your contains_multiple_collections method does not wait for the search to complete. You have two options I can think of. The first would involve some asynchronous callback implementation where the contains method does not block and perhaps takes a callback/listener object as an argument. The second is to make the contains method block until an outcome has been determined. I've outlined a sample implementation for latter approach below, it's not tested so be careful...
/*
* contains_multiple_collections now blocks until the desired
* value is located or all searches have completed unsuccessfully...
*/
private boolean contains_multiple_collections(V value) {
// thread pool
int numberProcessors = mCollections.size();
ExecutorService es = Executors.newFixedThreadPool(numberProcessors);
Object lock = new Object();
AtomicBoolean outcome = new AtomicBoolean(false);
AtomicInteger remainingSearches = new AtomicInteger(mCollections.size());
for (int i = 0; i < numberProcessors; i++) {
AbstractCollection<V> collection = mCollections.get(i);
es.submit(new MyRunnable(collection, value, lock, outcome, remainingSearches));
}
/* Wait for searches to run. This thread will be notified when all searches
* complete without successfully locating the value or as soon as the
* desired value is found.
*/
synchronized (lock) {
while (!outcome.get() && remainingSearches.get() > 0) {
try {
lock.wait();
} catch (InterruptedException ex) {
// do something sensible.
}
}
es.shutdownNow();
}
return outcome.get();
}
private class MyRunnable implements Runnable {
final AbstractCollection<V> mCollection;
final V mValue;
final Object lock;
final AtomicBoolean outcome;
final AtomicInteger remainingSearches;
public MyRunnable(AbstractCollection<V> mCollection, V mValue,
Object lock, AtomicBoolean outcome, AtomicInteger remainingSearches) {
this.mCollection = mCollection;
this.mValue = mValue;
this.lock = lock;
this.outcome = outcome;
this.remainingSearches = remainingSearches;
}
public void run() {
boolean ok = mCollection.contains(mValue);
if (ok || remainingSearches.decrementAndGet() == 0) {
synchronized (lock) {
if (ok) {
outcome.set(true);
}
lock.notify();
}
}
}
}
You could repeatedly loop through all the futures and poll them with get, using zero or almost zero timeout, but probably a better idea is to provide a callback to all your MyCallables, which should then call it when a match is found. The callback should then cancel all other tasks, maybe by shutting down the ExecutorService.
I suggest using a static AtomicBoolean which is set when a match is found. Each thread could then check the value before proceeding.
MySQL has a handy function:
SELECT GET_LOCK("SomeName")
This can be used to create simple, but very specific, name-based locks for an application. However, it requires a database connection.
I have many situations like:
someMethod() {
// do stuff to user A for their data for feature X
}
It doesn't make sense to simply synchronize this method, because, for example, if this method is called for user B in the meantime, user B does not need to wait for user A to finish before it starts, only operations for the user A and feature X combination need to wait.
With the MySql lock I could do something like:
someMethod() {
executeQuery("SELECT GET_LOCK('userA-featureX')")
// only locked for user A for their data for feature X
executeQuery("SELECT RELEASE_LOCK('userA-featureX')")
}
Since Java locking is based on objects, it seems like I would need to create a new object to represent the situation for this lock and then put it in a static cache somewhere so all the threads can see it. Subsequent requests to lock for that situation would then locate the lock object in the cache and acquire its lock. I tried to create something like this, but then the lock cache itself needs synchronization. Also, it is difficult to detect when a lock object is no longer being used so that it can be removed from the cache.
I have looked at the Java concurrent packages, but nothing stands out as being able to handle something like this. Is there an easy way to implement this, or am I looking at this from the wrong perspective?
Edit:
To clarify, I am not looking to create a predefined pool of locks ahead of time, I would like to create them on demand. Some pseudo-code for what I am thinking of is:
LockManager.acquireLock(String name) {
Lock lock;
synchronized (map) {
lock = map.get(name);
// doesn't exist yet - create and store
if(lock == null) {
lock = new Lock();
map.put(name, lock);
}
}
lock.lock();
}
LockManager.releaseLock(String name) {
// unlock
// if this was the last hold on the lock, remove it from the cache
}
All those answers I see are way too complicated. Why not simply use:
public void executeInNamedLock(String lockName, Runnable runnable) {
synchronized(lockName.intern()) {
runnable.run();
}
}
The key point is the method intern: it ensures that the String returned is a global unique object, and so it can be used as a vm-instance-wide mutex. All interned Strings are held in a global pool, so that's your static cache you were talking about in your original question. Don't worry about memleaks; those strings will be gc'ed if no other thread references it. Note however, that up to and including Java6 this pool is kept in PermGen space instead of the heap, so you might have to increase it.
There's a problem though if some other code in your vm locks on the same string for completely different reasons, but a) this is very unlikely, and b) you can get around it by introducing namespaces, e.g. executeInNamedLock(this.getClass().getName() + "_" + myLockName);
Can you have a Map<String, java.util.concurrent.Lock>? Each time you require a lock, you basically call map.get(lockName).lock().
Here's an example using Google Guava:
Map<String, Lock> lockMap = new MapMaker().makeComputingMap(new Function<String, Lock>() {
#Override public Lock apply(String input) {
return new ReentrantLock();
}
});
Then lockMap.get("anyOldString") will cause a new lock to be created if required and returned to you. You can then call lock() on that lock. makeComputingMap returns a Map that is thread-safe, so you can just share that with all your threads.
// pool of names that are being locked
HashSet<String> pool = new HashSet<String>();
lock(name)
synchronized(pool)
while(pool.contains(name)) // already being locked
pool.wait(); // wait for release
pool.add(name); // I lock it
unlock(name)
synchronized(pool)
pool.remove(name);
pool.notifyAll();
maybe this is useful for you: jkeylockmanager
Edit:
My initial response was probably a bit short. I am the author and was faced with this problem several times and could not find an existing solution. That's why I made this small library on Google Code.
Maybe a little later but you can use Google Guava Striped
Conceptually, lock striping is the technique of dividing a lock into many stripes, increasing the granularity of a single lock and allowing independent operations to lock different stripes and proceed concurrently, instead of creating contention for a single lock.
//init
stripes=Striped.lazyWeakLock(size);
//or
stripes=Striped.lock(size);
//...
Lock lock=stripes.get(object);
For locking on something like a user name, in-memory Locks in a map might be a bit leaky. As an alternative, you could look at using WeakReferences with WeakHashMap to create mutex objects that can be garbage collected when nothing refers to them. This avoids you having to do any manual reference counting to free up memory.
You can find an implementation here. Note that if you're doing frequent lookups on the map you may run into contention issues acquiring the mutex.
A generic solution using java.util.concurrent
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
public class LockByName<L> {
ConcurrentHashMap<String, L> mapStringLock;
public LockByName(){
mapStringLock = new ConcurrentHashMap<String, L>();
}
public LockByName(ConcurrentHashMap<String, L> mapStringLock){
this.mapStringLock = mapStringLock;
}
#SuppressWarnings("unchecked")
public L getLock(String key) {
L initValue = (L) createIntanceLock();
L lock = mapStringLock.putIfAbsent(key, initValue);
if (lock == null) {
lock = initValue;
}
return lock;
}
protected Object createIntanceLock() {
return new ReentrantLock();
}
public static void main(String[] args) {
LockByName<ReentrantLock> reentrantLocker = new LockByName<ReentrantLock>();
ReentrantLock reentrantLock1 = reentrantLocker.getLock("pepe");
try {
reentrantLock1.lock();
//DO WORK
}finally{
reentrantLock1.unlock();
}
}
}
Based on the answer of McDowell and his class IdMutexProvider, I have written the generic class LockMap that uses WeakHashMap to store lock objects. LockMap.get() can be used to retrieve a lock object for a key, which can then be used with the Java synchronized (...) statement to apply a lock. Unused lock objects are automatically freed during garbage collection.
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
// A map that creates and stores lock objects for arbitrary keys values.
// Lock objects which are no longer referenced are automatically released during garbage collection.
// Author: Christian d'Heureuse, www.source-code.biz
// Based on IdMutexProvider by McDowell, http://illegalargumentexception.blogspot.ch/2008/04/java-synchronizing-on-transient-id.html
// See also https://stackoverflow.com/questions/5639870/simple-java-name-based-locks
public class LockMap<KEY> {
private WeakHashMap<KeyWrapper<KEY>,WeakReference<KeyWrapper<KEY>>> map;
public LockMap() {
map = new WeakHashMap<KeyWrapper<KEY>,WeakReference<KeyWrapper<KEY>>>(); }
// Returns a lock object for the specified key.
public synchronized Object get (KEY key) {
if (key == null) {
throw new NullPointerException(); }
KeyWrapper<KEY> newKeyWrapper = new KeyWrapper<KEY>(key);
WeakReference<KeyWrapper<KEY>> ref = map.get(newKeyWrapper);
KeyWrapper<KEY> oldKeyWrapper = (ref == null) ? null : ref.get();
if (oldKeyWrapper != null) {
return oldKeyWrapper; }
map.put(newKeyWrapper, new WeakReference<KeyWrapper<KEY>>(newKeyWrapper));
return newKeyWrapper; }
// Returns the number of used entries in the map.
public synchronized int size() {
return map.size(); }
// KeyWrapper wraps a key value and is used in three ways:
// - as the key for the internal WeakHashMap
// - as the value for the internal WeakHashMap, additionally wrapped in a WeakReference
// - as the lock object associated to the key
private static class KeyWrapper<KEY> {
private KEY key;
private int hashCode;
public KeyWrapper (KEY key) {
this.key = key;
hashCode = key.hashCode(); }
public boolean equals (Object obj) {
if (obj == this) {
return true; }
if (obj instanceof KeyWrapper) {
return ((KeyWrapper)obj).key.equals(key); }
return false; }
public int hashCode() {
return hashCode; }}
} // end class LockMap
Example of how to use the LockMap class:
private static LockMap<String> lockMap = new LockMap<String>();
synchronized (lockMap.get(name)) {
...
}
A simple test program for the LockMap class:
public static Object lock1;
public static Object lock2;
public static void main (String[] args) throws Exception {
System.out.println("TestLockMap Started");
LockMap<Integer> map = new LockMap<Integer>();
lock1 = map.get(1);
lock2 = map.get(2);
if (lock2 == lock1) {
throw new Error(); }
Object lock1b = map.get(1);
if (lock1b != lock1) {
throw new Error(); }
if (map.size() != 2) {
throw new Error(); }
for (int i=0; i<10000000; i++) {
map.get(i); }
System.out.println("Size before gc: " + map.size()); // result varies, e.g. 4425760
System.gc();
Thread.sleep(1000);
if (map.size() != 2) {
System.out.println("Size after gc should be 2 but is " + map.size()); }
System.out.println("TestLockMap completed"); }
If anyone knows a better way to automatically test the LockMap class, please write a comment.
I'd like to notice that ConcurrentHashMap has built-in locking facility that is enough for simple exclusive multithread lock. No additional Lock objects needed.
Here is an example of such lock map used to enforce at most one active jms processing for single client.
private static final ConcurrentMap<String, Object> lockMap = new ConcurrentHashMap<String, Object>();
private static final Object DUMMY = new Object();
private boolean tryLock(String key) {
if (lockMap.putIfAbsent(key, DUMMY) != null) {
return false;
}
try {
if (/* attempt cluster-wide db lock via select for update nowait */) {
return true;
} else {
unlock(key);
log.debug("DB is already locked");
return false;
}
} catch (Throwable e) {
unlock(key);
log.debug("DB lock failed", e);
return false;
}
}
private void unlock(String key) {
lockMap.remove(key);
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {
String key = getClientKey(message);
if (tryLock(key)) {
try {
// handle jms
} finally {
unlock(key);
}
} else {
// key is locked, forcing redelivery
messageDrivenContext.setRollbackOnly();
}
}
2 years later but I was looking for a simple named locker solution and came across this, was usefull but I needed a simpler answer, so below what I came up with.
Simple lock under some name and release again under that same name.
private void doTask(){
locker.acquireLock(name);
try{
//do stuff locked under the name
}finally{
locker.releaseLock(name);
}
}
Here is the code:
public class NamedLocker {
private ConcurrentMap<String, Semaphore> synchSemaphores = new ConcurrentHashMap<String, Semaphore>();
private int permits = 1;
public NamedLocker(){
this(1);
}
public NamedLocker(int permits){
this.permits = permits;
}
public void acquireLock(String... key){
Semaphore tempS = new Semaphore(permits, true);
Semaphore s = synchSemaphores.putIfAbsent(Arrays.toString(key), tempS);
if(s == null){
s = tempS;
}
s.acquireUninterruptibly();
}
public void releaseLock(String... key){
Semaphore s = synchSemaphores.get(Arrays.toString(key));
if(s != null){
s.release();
}
}
}
Many implementations but non similar to mine.
Called my Dynamic lock implementation as ProcessDynamicKeyLock because it's a single process lock, for any object as key (equals+hashcode for uniqueness).
TODO: Add a way to provide the actual lock, for example, ReentrantReadWriteLock instead of ReentrantLock.
Implementation:
public class ProcessDynamicKeyLock<T> implements Lock
{
private final static ConcurrentHashMap<Object, LockAndCounter> locksMap = new ConcurrentHashMap<>();
private final T key;
public ProcessDynamicKeyLock(T lockKey)
{
this.key = lockKey;
}
private static class LockAndCounter
{
private final Lock lock = new ReentrantLock();
private final AtomicInteger counter = new AtomicInteger(0);
}
private LockAndCounter getLock()
{
return locksMap.compute(key, (key, lockAndCounterInner) ->
{
if (lockAndCounterInner == null) {
lockAndCounterInner = new LockAndCounter();
}
lockAndCounterInner.counter.incrementAndGet();
return lockAndCounterInner;
});
}
private void cleanupLock(LockAndCounter lockAndCounterOuter)
{
if (lockAndCounterOuter.counter.decrementAndGet() == 0)
{
locksMap.compute(key, (key, lockAndCounterInner) ->
{
if (lockAndCounterInner == null || lockAndCounterInner.counter.get() == 0) {
return null;
}
return lockAndCounterInner;
});
}
}
#Override
public void lock()
{
LockAndCounter lockAndCounter = getLock();
lockAndCounter.lock.lock();
}
#Override
public void unlock()
{
LockAndCounter lockAndCounter = locksMap.get(key);
lockAndCounter.lock.unlock();
cleanupLock(lockAndCounter);
}
#Override
public void lockInterruptibly() throws InterruptedException
{
LockAndCounter lockAndCounter = getLock();
try
{
lockAndCounter.lock.lockInterruptibly();
}
catch (InterruptedException e)
{
cleanupLock(lockAndCounter);
throw e;
}
}
#Override
public boolean tryLock()
{
LockAndCounter lockAndCounter = getLock();
boolean acquired = lockAndCounter.lock.tryLock();
if (!acquired)
{
cleanupLock(lockAndCounter);
}
return acquired;
}
#Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException
{
LockAndCounter lockAndCounter = getLock();
boolean acquired;
try
{
acquired = lockAndCounter.lock.tryLock(time, unit);
}
catch (InterruptedException e)
{
cleanupLock(lockAndCounter);
throw e;
}
if (!acquired)
{
cleanupLock(lockAndCounter);
}
return acquired;
}
#Override
public Condition newCondition()
{
LockAndCounter lockAndCounter = locksMap.get(key);
return lockAndCounter.lock.newCondition();
}
}
Simple test:
public class ProcessDynamicKeyLockTest
{
#Test
public void testDifferentKeysDontLock() throws InterruptedException
{
ProcessDynamicKeyLock<Object> lock = new ProcessDynamicKeyLock<>(new Object());
lock.lock();
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
try
{
new Thread(() ->
{
ProcessDynamicKeyLock<Object> anotherLock = new ProcessDynamicKeyLock<>(new Object());
anotherLock.lock();
try
{
anotherThreadWasExecuted.set(true);
}
finally
{
anotherLock.unlock();
}
}).start();
Thread.sleep(100);
}
finally
{
Assert.assertTrue(anotherThreadWasExecuted.get());
lock.unlock();
}
}
#Test
public void testSameKeysLock() throws InterruptedException
{
Object key = new Object();
ProcessDynamicKeyLock<Object> lock = new ProcessDynamicKeyLock<>(key);
lock.lock();
AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
try
{
new Thread(() ->
{
ProcessDynamicKeyLock<Object> anotherLock = new ProcessDynamicKeyLock<>(key);
anotherLock.lock();
try
{
anotherThreadWasExecuted.set(true);
}
finally
{
anotherLock.unlock();
}
}).start();
Thread.sleep(100);
}
finally
{
Assert.assertFalse(anotherThreadWasExecuted.get());
lock.unlock();
}
}
}
Another possible solution which I have implemented and tested when encountered the same requirements as the original poster.
In this solution:
No external libraries
Not leaving unused objects in memory
Minimal usage of synchronized and minimal "cross-names" locking
No downsides of using intern
Helper class code:
public class IdBasedLockHelper<T> {
private final static AtomicIntegerWithEquals zero = new AtomicIntegerWithEquals(0);
private final ConcurrentMap<T, AtomicIntegerWithEquals> identifierToLockCounter = new ConcurrentHashMap<>();
public void executeLocked(T lockId, Runnable runnable) {
AtomicIntegerWithEquals counterAndLock = identifierToLockCounter.compute(lockId, (key, existing) -> {
if (existing == null) {
return new AtomicIntegerWithEquals(1);
}
existing.atomicValue.incrementAndGet();
return existing;
});
synchronized (counterAndLock) {
try {
runnable.run();
} finally {
counterAndLock.atomicValue.decrementAndGet();
identifierToLockCounter.remove(lockId, zero);
}
}
}
// AtomicInteger does not implement equals() properly so there is a need for such wrapper
private static class AtomicIntegerWithEquals {
private final AtomicInteger atomicValue;
AtomicIntegerWithEquals(int value) {
this.atomicValue = new AtomicInteger(value);
}
// Used internally by remove()
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IdBasedLockHelper.AtomicIntegerWithEquals)) return false;
return atomicValue.get() == ((AtomicIntegerWithEquals) o).atomicValue.get();
}
// Not really used, but when implementing custom equals() it is a good practice to implement also hashCode()
#Override
public int hashCode() {
return atomicValue.get();
}
}
}
Usage:
IdBasedLockHelper<String> idBasedLockHelper = new IdBasedLockHelper<>();
idBasedLockHelper.executeLocked("Some Name", () -> {
// Your code to execute synchronized per name
});
ConcurrentHashMap is used to store synchronization object for each lock id.
ConcurrentHashMap already provides compute and remove (if value equals) as atomic operations. The AtomicInteger inside the stored value counts the number of holds of the synchronization object and this allows removing it from the map only if it is not in use (number of holds equals 0).
Maybe something like that:
public class ReentrantNamedLock {
private class RefCounterLock {
public int counter;
public ReentrantLock sem;
public RefCounterLock() {
counter = 0;
sem = new ReentrantLock();
}
}
private final ReentrantLock _lock = new ReentrantLock();
private final HashMap<String, RefCounterLock> _cache = new HashMap<String, RefCounterLock>();
public void lock(String key) {
_lock.lock();
RefCounterLock cur = null;
try {
if (!_cache.containsKey(key)) {
cur = new RefCounterLock();
_cache.put(key, cur);
} else {
cur = _cache.get(key);
}
cur.counter++;
} finally {
_lock.unlock();
}
cur.sem.lock();
}
public void unlock(String key) {
_lock.lock();
try {
if (_cache.containsKey(key)) {
RefCounterLock cur = _cache.get(key);
cur.counter--;
cur.sem.unlock();
if (cur.counter == 0) { //last reference
_cache.remove(key);
}
cur = null;
}
} finally {
_lock.unlock();
}
}}
I didn't test it though.
After some disappointment that there is no language level support or simple Guava/Commons class for named locks,
This is what I settled down to:
ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();
Object getLock(String name) {
Object lock = locks.get(name);
if (lock == null) {
Object newLock = new Object();
lock = locks.putIfAbsent(name, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
void somethingThatNeedsNamedLocks(String name) {
synchronized(getLock(name)) {
// some operations mutually exclusive per each name
}
}
Here I achieved: little boilerplate code with no library dependency, atomically acquiring the lock object, not polluting the global interned string objects, no low-level notify/wait chaos and no try-catch-finally mess.
Similar to the answer from Lyomi, but uses the more flexible ReentrantLock instead of a synchronized block.
public class NamedLock
{
private static final ConcurrentMap<String, Lock> lockByName = new ConcurrentHashMap<String, Lock>();
public static void lock(String key)
{
Lock lock = new ReentrantLock();
Lock existingLock = lockByName.putIfAbsent(key, lock);
if(existingLock != null)
{
lock = existingLock;
}
lock.lock();
}
public static void unlock(String key)
{
Lock namedLock = lockByName.get(key);
namedLock.unlock();
}
}
Yes this will grow over time - but using the ReentrantLock opens up greater possibilities for removing the lock from the map. Although, removing items from the map doesn't seem all that useful considering removing values from the map will not shrink its size. Some manual map sizing logic would have to be implemented.
Memory consideration
Often times, synchronization needed for a particular key is short-lived. Keeping around released keys can lead to excessive memory waste, making it non-viable.
Here's an implementation does not internally keep around released keys.
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
public class KeyedMutexes<K> {
private final ConcurrentMap<K, CountDownLatch> key2Mutex = new ConcurrentHashMap<>();
public void lock(K key) throws InterruptedException {
final CountDownLatch ourLock = new CountDownLatch(1);
for (;;) {
CountDownLatch theirLock = key2Mutex.putIfAbsent(key, ourLock);
if (theirLock == null) {
return;
}
theirLock.await();
}
}
public void unlock(K key) {
key2Mutex.remove(key).countDown();
}
}
Reentrancy, and other bells and whistles
If one wants re-entrant lock semantics, they can extend the above by wrapping the mutex object in a class that keeps track of the locking thread and locked count.
e.g.:
private static class Lock {
final CountDownLatch mutex = new CountDownLatch(1);
final long threadId = Thread.currentThread().getId();
int lockedCount = 1;
}
If one wants lock() to return an object to make releases easier and safer, that's also a possibility.
Putting it all together, here's what the class could look like:
public class KeyedReentrantLocks<K> {
private final ConcurrentMap<K, KeyedLock> key2Lock = new ConcurrentHashMap<>();
public KeyedLock acquire(K key) throws InterruptedException {
final KeyedLock ourLock = new KeyedLock() {
#Override
public void close() {
if (Thread.currentThread().getId() != threadId) {
throw new IllegalStateException("wrong thread");
}
if (--lockedCount == 0) {
key2Lock.remove(key);
mutex.countDown();
}
}
};
for (;;) {
KeyedLock theirLock = key2Lock.putIfAbsent(key, ourLock);
if (theirLock == null) {
return ourLock;
}
if (theirLock.threadId == Thread.currentThread().getId()) {
theirLock.lockedCount++;
return theirLock;
}
theirLock.mutex.await();
}
}
public static abstract class KeyedLock implements AutoCloseable {
protected final CountDownLatch mutex = new CountDownLatch(1);
protected final long threadId = Thread.currentThread().getId();
protected int lockedCount = 1;
#Override
public abstract void close();
}
}
And here's how one might use it:
try (KeyedLock lock = locks.acquire("SomeName")) {
// do something critical here
}
In response to the suggestion of using new MapMaker().makeComputingMap()...
MapMaker().makeComputingMap() is deprecated for safety reasons. The successor is CacheBuilder. With weak keys/values applied to CacheBuilder, we're soooo close to a solution.
The problem is a note in CacheBuilder.weakKeys():
when this method is used, the resulting cache will use identity (==) comparison to determine equality of keys.
This makes it impossible to select an existing lock by string value. Erg.
(4 years later...)
My answer is similar to user2878608's but I think there are some missing edge cases in that logic. I also thought Semaphore was for locking multiple resources at once (though I suppose using it for counting lockers like that is fine too), so I used a generic POJO lock object instead. I ran one test on it which demonstrated each of the edge cases existed IMO and will be using it on my project at work. Hope it helps someone. :)
class Lock
{
int c; // count threads that require this lock so you don't release and acquire needlessly
}
ConcurrentHashMap<SomeKey, Lock> map = new ConcurrentHashMap<SomeKey, Lock>();
LockManager.acquireLock(String name) {
Lock lock = new Lock(); // creating a new one pre-emptively or checking for null first depends on which scenario is more common in your use case
lock.c = 0;
while( true )
{
Lock prevLock = map.putIfAbsent(name, lock);
if( prevLock != null )
lock = prevLock;
synchronized (lock)
{
Lock newLock = map.get(name);
if( newLock == null )
continue; // handles the edge case where the lock got removed while someone was still waiting on it
if( lock != newLock )
{
lock = newLock; // re-use the latest lock
continue; // handles the edge case where a new lock was acquired and the critical section was entered immediately after releasing the lock but before the current locker entered the sync block
}
// if we already have a lock
if( lock.c > 0 )
{
// increase the count of threads that need an offline director lock
++lock.c;
return true; // success
}
else
{
// safely acquire lock for user
try
{
perNameLockCollection.add(name); // could be a ConcurrentHashMap or other synchronized set, or even an external global cluster lock
// success
lock.c = 1;
return true;
}
catch( Exception e )
{
// failed to acquire
lock.c = 0; // this must be set in case any concurrent threads are waiting
map.remove(name); // NOTE: this must be the last critical thing that happens in the sync block!
}
}
}
}
}
LockManager.releaseLock(String name) {
// unlock
// if this was the last hold on the lock, remove it from the cache
Lock lock = null; // creating a new one pre-emptively or checking for null first depends on which scenario is more common in your use case
while( true )
{
lock = map.get(name);
if( lock == null )
{
// SHOULD never happen
log.Error("found missing lock! perhaps a releaseLock call without corresponding acquireLock call?! name:"+name);
lock = new Lock();
lock.c = 1;
Lock prevLock = map.putIfAbsent(name, lock);
if( prevLock != null )
lock = prevLock;
}
synchronized (lock)
{
Lock newLock = map.get(name);
if( newLock == null )
continue; // handles the edge case where the lock got removed while someone was still waiting on it
if( lock != newLock )
{
lock = newLock; // re-use the latest lock
continue; // handles the edge case where a new lock was acquired and the critical section was entered immediately after releasing the lock but before the current locker entered the sync block
}
// if we are not the last locker
if( lock.c > 1 )
{
// decrease the count of threads that need an offline director lock
--lock.c;
return true; // success
}
else
{
// safely release lock for user
try
{
perNameLockCollection.remove(name); // could be a ConcurrentHashMap or other synchronized set, or even an external global cluster lock
// success
lock.c = 0; // this must be set in case any concurrent threads are waiting
map.remove(name); // NOTE: this must be the last critical thing that happens in the sync block!
return true;
}
catch( Exception e )
{
// failed to release
log.Error("unable to release lock! name:"+name);
lock.c = 1;
return false;
}
}
}
}
}
I've created a tokenProvider based on the IdMutexProvider of McDowell.
The manager uses a WeakHashMap which takes care of cleaning up unused locks.
TokenManager:
/**
* Token provider used to get a {#link Mutex} object which is used to get exclusive access to a given TOKEN.
* Because WeakHashMap is internally used, Mutex administration is automatically cleaned up when
* the Mutex is no longer is use by any thread.
*
* <pre>
* Usage:
* private final TokenMutexProvider<String> myTokenProvider = new TokenMutexProvider<String>();
*
* Mutex mutex = myTokenProvider.getMutex("123456");
* synchronized (mutex) {
* // your code here
* }
* </pre>
*
* Class inspired by McDowell.
* url: http://illegalargumentexception.blogspot.nl/2008/04/java-synchronizing-on-transient-id.html
*
* #param <TOKEN> type of token. It is important that the equals method of that Object return true
* for objects of different instances but with the same 'identity'. (see {#link WeakHashMap}).<br>
* E.g.
* <pre>
* String key1 = "1";
* String key1b = new String("1");
* key1.equals(key1b) == true;
*
* or
* Integer key1 = 1;
* Integer key1b = new Integer(1);
* key1.equals(key1b) == true;
* </pre>
*/
public class TokenMutexProvider<TOKEN> {
private final Map<Mutex, WeakReference<Mutex>> mutexMap = new WeakHashMap<Mutex, WeakReference<Mutex>>();
/**
* Get a {#link Mutex} for the given (non-null) token.
*/
public Mutex getMutex(TOKEN token) {
if (token==null) {
throw new NullPointerException();
}
Mutex key = new MutexImpl(token);
synchronized (mutexMap) {
WeakReference<Mutex> ref = mutexMap.get(key);
if (ref==null) {
mutexMap.put(key, new WeakReference<Mutex>(key));
return key;
}
Mutex mutex = ref.get();
if (mutex==null) {
mutexMap.put(key, new WeakReference<Mutex>(key));
return key;
}
return mutex;
}
}
public int size() {
synchronized (mutexMap) {
return mutexMap.size();
}
}
/**
* Mutex for acquiring exclusive access to a token.
*/
public static interface Mutex {}
private class MutexImpl implements Mutex {
private final TOKEN token;
protected MutexImpl(TOKEN token) {
this.token = token;
}
#Override
public boolean equals(Object other) {
if (other==null) {
return false;
}
if (getClass()==other.getClass()) {
TOKEN otherToken = ((MutexImpl)other).token;
return token.equals(otherToken);
}
return false;
}
#Override
public int hashCode() {
return token.hashCode();
}
}
}
Usage:
private final TokenMutexManager<String> myTokenManager = new TokenMutexManager<String>();
Mutex mutex = myTokenManager.getMutex("UUID_123456");
synchronized(mutex) {
// your code here
}
or rather use Integers?
private final TokenMutexManager<Integer> myTokenManager = new TokenMutexManager<Integer>();
Mutex mutex = myTokenManager.getMutex(123456);
synchronized(mutex) {
// your code here
}
This thread is old, but a possible solution is the framework https://github.com/brandaof/named-lock.
NamedLockFactory lockFactory = new NamedLockFactory();
...
Lock lock = lockFactory.getLock("lock_name");
lock.lock();
try{
//manipulate protected state
}
finally{
lock.unlock();
}
Here is a simple and optimized solution which addresses the removal of used locks also, but with an overhead of synchronization of the Map:
public class NamedLock {
private Map<String, ReentrantLock> lockMap;
public NamedLock() {
lockMap = new HashMap<>();
}
public void lock(String... name) {
ReentrantLock newLock = new ReentrantLock(true);
ReentrantLock lock;
synchronized (lockMap) {
lock = Optional.ofNullable(lockMap.putIfAbsent(Arrays.toString(name), newLock)).orElse(newLock);
}
lock.lock();
}
public void unlock(String... name) {
ReentrantLock lock = lockMap.get(Arrays.toString(name));
synchronized (lockMap) {
if (!lock.hasQueuedThreads()) {
lockMap.remove(name);
}
}
lock.unlock();
}
}
Your idea about a shared static repository of lock objects for each situation is correct.
You don't need the cache itself to be synchronized ... it can be as simple as a hash map.
Threads can simultaneously get a lock object from the map. The actual synchronization logic should be encapsulated within each such object separately (see the java.util.concurrent package for that - http://download.oracle.com/javase/6/docs/api/java/util/concurrent/locks/package-summary.html)
TreeMap because in HashMap size of inner array can only increase
public class Locker<T> {
private final Object lock = new Object();
private final Map<T, Value> map = new TreeMap<T, Value>();
public Value<T> lock(T id) {
Value r;
synchronized (lock) {
if (!map.containsKey(id)) {
Value value = new Value();
value.id = id;
value.count = 0;
value.lock = new ReentrantLock();
map.put(id, value);
}
r = map.get(id);
r.count++;
}
r.lock.lock();
return r;
}
public void unlock(Value<T> r) {
r.lock.unlock();
synchronized (lock) {
r.count--;
if (r.count == 0)
map.remove(r.id);
}
}
public static class Value<T> {
private Lock lock;
private long count;
private T id;
}
}