Concurrency issues. Threads have their own call stack, but can also access shared data. Therefore you have two basic problems, visibility and access problems. A visibility problem occurs if thread A reads shared data which is later changed by thread B and thread A is unaware of this change.
When multiple transactions execute concurrently in an uncontrolled or unrestricted manner, then it might lead to several problems. These problems are commonly referred to as concurrency problems in a database environment.
The main way we can avoid such concurrency issues and build reliable code is to work with immutable objects. This is because their state cannot be modified by the interference of multiple threads. However, we can't always work with immutable objects.
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");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With