Confused about what pattern to use to block threads conditionally - java

I'm having a Map object that could be null or simply cleared when the application first starts. I need all threads accessing this map to block till the map is initialized and only then I need to signal all threads to access this map.
This map holds configuration data and it will be for reading only unless a single threads decides to refresh to load new configuration data (So it doesn't need to Synchronized for the sake of performance as I don't find necessary too). I tried using a Condition object for a ReentrantLock but it threw IllegalMonitorState exceptions whenever I tried to signalAll() or await().
Here is a pseudo code for what I need to do:
void monitorThread{
while(someCondition){
map = updatedMap();
condition.signalAll();
}
}
String readValueFromMap(String key){
if(map == null){
condition.await();
}
return map.get(key);
}

CountDownLatch is all you need.
CountDownLatch latch = new CountDownLatch(1);
While initialize hashmap do latch.countdown() and in threads use latch.await()
void monitorThread{
map = updatedMap();
latch.countDown();
}
String readValueFromMap(String key){
latch.await();
return map.get(key);
}
Please note that CountDownLatch await() method only waits if countdown is greater than 0 hence only first time.

To do this right, you need a memory barrier hence the volatile. Because the map may be null initially, you are going to need another lock object. The following should work:
private final Object lockObject = new Object();
private volatile Map<...> map;
void monitorThread{
while (condition){
// do this outside of the synchronized in case it takes a while
Map<...> updatedMap = updatedMap();
synchronized (lockObject) {
map = updatedMap;
// notify everyone that may be waiting for the map to be initialized
lockObject.notifyAll();
}
}
}
String readValueFromMap(String key) {
// we grab a copy of the map to avoid race conditions in case the map is
// updated in the future
Map<...> mapRef = map;
// we have a while loop here to handle spurious signals
if (mapRef == null) {
synchronized (lockObject) {
while (map == null) {
// wait for the map to initialized
lockObject.wait();
}
mapRef = map;
}
}
return mapRef.get(key);
}

Sounds like all you need is a "Lock" object that guards access to the Map.
These are pretty easy to use:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
You could probably use: java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock

Related

Using concurrentHashMap vs Hashmap

private static final Map<String, SampleClass> map = new
ConcurrentHashMap<>();
public static SampleClass getsampleclass(String context) {
if( map.get(context) != null) {
return map.get(context);
} else {
SampleClass cls = new SampleClass(context);
map.put(context, cls);
}
}
In multi-threaded environment, if two threads get map.get(context) as null, then both the threads will create cls, and put will blocked, therefore thread1 will put first and after it thread2 will override what was put by thread1.
Is this behavior correct ?
In my case, I want same value to be returned when map.get is done, hence i see using HashMap and synchronizing it is preferred.
Use CHM's atomic computeIfAbsent() method and you won't have to worry about synchronization:
return map.computeIfAbsent(context, SampleClass::new);

Replace double checked locking in concurrent environment

I have already topic with same code:
public abstract class Digest {
private Map<String, byte[]> cache = new HashMap<>();
public byte[] digest(String input) {
byte[] result = cache.get(input);
if (result == null) {
synchronized (cache) {
result = cache.get(input);
if (result == null) {
result = doDigest(input);
cache.put(input, result);
}
}
}
return result;
}
protected abstract byte[] doDigest(String input);
}
At previous I got prove that code is not thread safe.
At this topic I want to provide solutions which I have in my head and I ask you to review these solutions:
Solution#1 through ReadWriteLock:
public abstract class Digest {
private final ReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock readLock = rwl.readLock();
private final Lock writeLock = rwl.writeLock();
private Map<String, byte[]> cache = new HashMap<>(); // I still don't know should I use volatile or not
public byte[] digest(String input) {
byte[] result = null;
readLock.lock();
try {
result = cache.get(input);
} finally {
readLock.unlock();
}
if (result == null) {
writeLock.lock();
try {
result = cache.get(input);
if (result == null) {
result = doDigest(input);
cache.put(input, result);
}
} finally {
writeLock.unlock();
}
}
return result;
}
protected abstract byte[] doDigest(String input);
}
Solution#2 through CHM
public abstract class Digest {
private Map<String, byte[]> cache = new ConcurrentHashMap<>(); //should be volatile?
public byte[] digest(String input) {
return cache.computeIfAbsent(input, this::doDigest);
}
protected abstract byte[] doDigest(String input);
}
Please review correctness of both solutions. It is not question about what the solution better. I undestand that CHM better. Please, review correctnes of implementation
Unlike the clusterfudge we got into in the last question, this is better.
As was shown in the prefious question's duplicate, the original code is not thread-safe since HashMap is not threadsafe and the initial get() can be called while the put() is being executed inside the synchronized block. This can break all sorts of things, so that's definitely not threadsafe.
The second solution is thread-safe, since all accesses to cache are done in guarded code. The inital get() is protected by a readlock, and the put() is done inside a writelock, guaranteeing that threads can't read the cache while it's being written to, but they're free to read it at the same time as other reading threads. No concurrency issues, no visibility issues, no chances of deadlocks. Everything's fine.
The last is of course the most elegant one. Since computeIfAbsent() is an atomic operation, it guarantees that the value is either directly returned or computed at most once, from the javadoc:
If the specified key is not already associated with a value, attempts
to compute its value using the given mapping function and enters it
into this map unless null. The entire method invocation is
performed atomically, so the function is applied at most once per key.
Some attempted update operations on this map by other threads may be
blocked while computation is in progress, so the computation should be
short and simple, and must not attempt to update any other mappings of
this map.
The Map in question shouldn't be volatile, but it should be final. If it's not final, it could (at least in theory) be changed and it would be possible for 2 threads to work on different objects, which is not what you want.

How to do "synchronized-with-resource" where obtaining the lock object must also be synchronized?

I have a Map<String, Mutex> m that I would like to lock on the Mutex, while also ensuring that the act of obtaining the lock is thread-safe:
//Assuming till here m.get(key) return a non-null value
synchronized (Mutex mtx = m.get(key)) {
mtx.count++;
//...
}
The following is not guaranteed in Java
Mutex mtx = m.get(key);
synchroinzed (mtx) {
//thread 2 may acquire lock before thread 1 even thread 1 execute first
}
and neither is
synchronized (m.get(key)) {
Mutex mtx = m.get(key);
//mtx could be null if other threads remove the key between 2 statements
//because lock is only acquired on m.get(key), not m itself
}
How can I safely achieve this?
Edit:
My exact code right now:
public static <T, V> void lock (Map<T, Mutex<V>> mm, T uniqueKeyValue1, long waitMs) throws Mutex.WaitTimeoutException, InterruptedException {
synchronized ( MapUtils.putIfAbsentRetNewVal(mm, uniqueKeyValue1, new Mutex<V>()) ) {
Mutex<V> m = mm.get(uniqueKeyValue1);
if (m.getLockCount() == 0) {
m.incrementLockCount();
}
else {
m.incrementLockCount();
m.wait(waitMs);
if (m.getValue() == null) {
throw new Mutex.WaitTimeoutException();
}
}
}
}
This is a non-problem, and it doesn't have anything to do with with-resources. Use option 2. What you're asking for won't solve that problem. What you would need is not a with-resources but something that would synchronize on the map, then synchronize on the mutex, then release the synchronization on the map. You can't accompish that with synchronization because of the static scoping.
What you need is in fact another Mutex. Or, just accept the race. It's benign. You don't care which thread executes first.
The language need not have a "synchronize with resources" construct because the JVM guarantees that it will release the lock when the synchronized block exits.
In this scenario you have to lock on the map so no other thread can modify it while the current thread is using it ( m.get(key) ), perform your operation and then release the lock.
Map<String, Mutex> m = ...
synchronized( m ) { // only a single thread can access m now
Mutex mtx = m.get(key);
mtx.count++;
} // releases the lock

Collections.synchronizedMap(Map) after migration to Linux - second Thread doesn't see new entry

I have the following issue after trying to run my webapplication on Linux server.
When running on windows, everything works perfectly (simplified version) - call send() method, wait for JMS response on synchronizer object, send the response to client)...
When started on linux server (same JVM version - 1.7, bytecode - java 1.5 version), I get response only for the first message, and following error in log for the rest of the messages:
synchronizer is null /*my_generated_message_id*/
It looks like JMS message listener thread cannot see new entries (created in JMS sender Thread) in synchronizers map, but I don't understand why...
Synchronizers Map definition:
public final Map<String, ReqRespSynchro<Map>> synchronizers
= Collections.synchronizedMap(new HashMap<String, ReqRespSynchro<Map>>());
Sending JMS request with active response awaiting:
#Override
public Map send(Map<String,Object> params) {
String msgIdent = ""/*my_generated_message_id*/;
Map response = null;
ReqRespSynchro<Map> synchronizer = synchronizers.get(msgIdent);
if (synchronizer == null) {
synchronizer = new ReqRespSynchro<Map>();
synchronizers.put(msgIdent , synchronizer);
}
synchronized(synchronizer) {
try {
sender.send(params);
} catch (Exception ex) {
log.error("send error", ex);
}
synchronizer.initSendSequence();
int iter = 1;
try {
while (!synchronizer.isSet() && iter > 0) {
synchronizer.wait(this.waitTimeout);
iter--;
}
} catch (Exception ex) {
log.error("send error 2", ex);
return null;
} finally {
response = (synchronizers.remove(msgIdent )).getRespObject();
}
}
return response;
}
JMS onMessage response processing (separate thread):
public void onMessage(Message msg) {
Map<String,Object> response = (Map<String,Object>) om.getObject();
String msgIdent = response.getMyMsgID(); ///*my_generated_message_id*/
ReqRespSynchro<Map> synchronizer = synchronizers.get(msgIdent);
if (synchronizer != null) {
synchronized (synchronizer) {
msgSynchronizer.setRespObject(response);
synchronizer.notify();
}
} else {
log.error("synchronizer is null " + msgIdent);
}
}
Synchronizer class:
public class ReqRespSynchro<E> {
private E obj = null;
public synchronized void setRespObject(E obj) {
this.obj = obj;
}
public synchronized void initSendSequence() {
this.obj = null;
}
public synchronized boolean isSet() {
return this.obj != null;
}
public synchronized E getRespObject() {
E ret = null;
ret = obj;
return ret;
}
}
Your code bears the “check-then-act” anti-pattern.
ReqRespSynchro<Map> synchronizer = synchronizers.get(msgIdent);
if (synchronizer == null) {
synchronizer = new ReqRespSynchro<Map>();
synchronizers.put(msgIdent , synchronizer);
}
Here, you first check whether the synchronizers contains a particular mapping then you act by putting a new mapping when the mapping is not present, but by the time you act, there is no guaranty that the condition you have checked still holds.
While the map returned by Collections.synchronizedMap guarantees thread-safe put and get methods, it does not (and can’t) guaranty that there won’t be an update between subsequent invocation of get and put.
So if two threads execute the code above, there is the possibility that one thread puts a new value while the other already has performed the get operation but not the put operation and will therefore proceed with putting a new value, overwriting the existing. So the threads will use different ReqRespSynchro instances and so will the other threads get either of these from the map.
The correct use would be to synchronize the entire compound operation:
synchronized(synchronizers) {
ReqRespSynchro<Map> synchronizer = synchronizers.get(msgIdent);
if (synchronizer == null) {
synchronizer = new ReqRespSynchro<Map>();
synchronizers.put(msgIdent , synchronizer);
}
}
It’s a common mistake to think that by wrapping a map or collection into a synchronized one, every thread safety issue was solved. But you still have to think about access patterns and guard compound operations manually, so sometimes you’re better off using manual locking only and resist the temptation of easy-to-use synchronized wrappers.
But note the ConcurrentMap was added to the Java API to address this use pattern (amongst others).
Change the map declaration to
public final ConcurrentHashMap<String, ReqRespSynchro<Map>> synchronizers
= new ConcurrentHashMap<>();
This map provides thread safe put and get methods, but also methods allowing to avoid the “check-then-act” anti-pattern for updates.
Using the ConcurrentMap under Java 8 is especially easy:
ReqRespSynchro<Map> synchronizer = synchronizers
.computeIfAbsent(msgIdent, key -> new ReqRespSynchro<>());
The invocation of computeIfAbsent will get the ReqRespSynchro<Map>, if there is one, otherwise the provided function will be executed to compute a value which will get stored, all with atomicity guaranty. The places where you simply get an existing instance need no change.
The pre-Java 8 code is a bit more convoluted:
ReqRespSynchro<Map> synchronizer = synchronizers.get(msgIdent);
if (synchronizer == null) {
synchronizer = new ReqRespSynchro<>();
ReqRespSynchro<Map> concurrent = synchronizers.putIfAbsent(msgIdent , synchronizer);
if(concurrent!=null) synchronizer = concurrent;
}
Here, we can’t perform the operation atomically, but we are able to detect if a concurrent update happened in-between. In this case, putIfAbsent will not modify the map but return the value already contained in the map. So if we encounter such a situation, all we have to do is to use that existing one instead of the one we attempted to put.
This could happen if your waitTimeout in send() method is too short. You only have one iteration set for the waiting cycle. So the msgIdent entry may be removed from the map in finally block in send before it can be read in onMessage(): wait timeout expires, iteration counter is decremented, thread exits the cycle and removes the entry from map.
Even if waitTimeout is long enough you may experience a so-called spurious wakeup:
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied.
By the way why don't you send response back via JMS without some cryptic synchronization? Here is an example for ActiveMQ message broker: How should I implement request response with JMS?

Two BlockingQueue - deadlock

I have a requirement to manipulate two queues atomically and am not sure what is the correct synchronization strategy: This is what I was trying:
public class transfer {
BlockingQueue firstQ;
BlockingQueue secondQ;
public moveToSecond() {
synchronized (this){
Object a = firstQ.take();
secondQ.put(a)
}
}
public moveToFirst() {
synchronized(this) {
Object a = secondQ.take();
firstQ.put(a);
}
}
}
Is this the correct pattern? In the method moveToSecond(), if firstQ is empty, the method will wait on firstQ.take(), but it still holds the lock on this object. This will prevent moveToFirst() to have a chance to execute.
I am confused about the lock release during a wait - Does the thread release all locks [both this and BlockedQUeue lock?]? What is the correct pattern to provide atomicity dealing with multiple blocking queues?
You are using the correct approach using a common mutex to synchronize between both queues. However, to avoid the situation you describe with the first queue being empty I'd suggest reimplementing moveToFirst() and moveToSecond() to use poll() rather than take(); e.g.
public void boolean moveToFirst() {
// Synchronize on simple mutex; could use a Lock here but probably
// not worth the extra dev. effort.
synchronzied(queueLock) {
boolean success;
// Will return immediately, returning null if the queue is empty.
Object o = firstQ.poll();
if (o != null) {
// Put could block if the queue is full. If you're using a bounded
// queue you could use add(Object) instead to avoid any blocking but
// you would need to handle the exception somehow.
secondQ.put(o);
success = true;
} else {
success = false;
}
}
return success;
}
Another failure condition you didn't mention is if firstQ is not empty but secondQ is full, the item will be removed from firstQ but there will be no place to put it.
So the only correct way is to use poll and offer with timeouts and code to return things to the way they were before any failure (important!), then retry after a random time until both poll and offer are successful.
This is an optimistic approach; efficient in normal operation but quite inefficient when deadlocks are frequent (average latency depends on the timeout chosen)
You should use the Lock-mechanism from java.util.concurrency, like this:
Lock lock = new ReentrantLock();
....
lock.lock();
try {
secondQ.put(firstQ.take());
} finally {
lock.unlock();
}
Do the same for firstQ.put(secondQ.take()), using the same lock object.
There is no need to use the lowlevel wait/notify methods on the Object class anymore, unless you are writing new concurrency primitives.
In your code, while the thread is blocked on BlockingQueue.take() it is holding on to the lock on this. The lock isn't released until either the code leaves the synchronized block or this.wait() is called.
Here I assume that moveToFirst() and moveToSecond() should block, and that your class controls all access to the queues.
private final BlockingQueue<Object> firstQ = new LinkedBlockingQueue();
private final Semaphore firstSignal = new Semaphore(0);
private final BlockingQueue<Object> secondQ = LinkedBlockingQueue();
private final Semaphore secondSignal = new Semaphore(0);
private final Object monitor = new Object();
public void moveToSecond() {
int moved = 0;
while (moved == 0) {
// bock until someone adds to the queue
firstSignal.aquire();
// attempt to move an item from one queue to another atomically
synchronized (monitor) {
moved = firstQ.drainTo(secondQ, 1);
}
}
}
public void putInFirst(Object object) {
firstQ.put(object);
// notify any blocking threads that the queue has an item
firstSignal.release();
}
You would have similar code for moveToFirst() and putInSecond(). The while is only needed if some other code might remove items from the queue. If you want the method that removes on the queue to wait for pending moves, it should aquire a permit from the semaphore, and the semaphore should be created as a fair Semaphore, so the first thread to call aquire will get released first:
firstSignal = new Semaphore(0, true);
If you don't want moveToFirst() to block you have a few options
Have the method do do its work in a Runnable sent to an Executor
Pass a timeout to moveToFirst() and use BlockingQueue.poll(int, TimeUnit)
Use BlockingQueue.drainTo(secondQ, 1) and modify moveToFirst() to return a boolean to indicate if it was successful.
For the above three options, you wouldn't need the semaphore.
Finally, I question the need to make the move atomic. If multiple threads are adding or removing from the queues, then an observing queue wouldn't be able to tell whether moveToFirst() was atomic.

Categories