What is the most frequent concurrency issue you've encountered in Java? [closed]
My #1 most painful concurrency problem ever occurred when two different open source libraries did something like this:
private static final String LOCK = "LOCK"; // use matching strings
// in two different libraries
public doSomestuff() {
synchronized(LOCK) {
this.work();
}
}
At first glance, this looks like a pretty trivial synchronization example. However; because Strings are interned in Java, the literal string "LOCK"
turns out to be the same instance of java.lang.String
(even though they are declared completely disparately from each other.) The result is obviously bad.
The most common concurrency problem I've seen, is not realizing that a field written by one thread is not guaranteed to be seen by a different thread. A common application of this:
class MyThread extends Thread {
private boolean stop = false;
public void run() {
while(!stop) {
doSomeWork();
}
}
public void setStop() {
this.stop = true;
}
}
As long as stop is not volatile or setStop
and run
are not synchronized this is not guaranteed to work. This mistake is especially devilish as in 99.999% it won't matter in practice as the reader thread will eventually see the change - but we don't know how soon he saw it.
One classic problem is changing the object you're synchronizing on while synchronizing on it:
synchronized(foo) {
foo = ...
}
Other concurrent threads are then synchronizing on a different object and this block does not provide the mutual exclusion you expect.
A common problem is using classes like Calendar and SimpleDateFormat from multiple threads (often by caching them in a static variable) without synchronization. These classes are not thread-safe so multi-threaded access will ultimately cause strange problems with inconsistent state.
Not properly synchronizing on objects returned by Collections.synchronizedXXX()
, especially during iteration or multiple operations:
Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());
...
if(!map.containsKey("foo"))
map.put("foo", "bar");
That's wrong. Despite single operations being synchronized
, state of map between invoking contains
and put
can be changed by another thread. It should be:
synchronized(map) {
if(!map.containsKey("foo"))
map.put("foo", "bar");
}
Or with a ConcurrentMap
implementation:
map.putIfAbsent("foo", "bar");