Thread safety in java
All,
I started learning Java threads in the past few days and have only read about scenarios where even after using synchronizer methods/blocks, the code/class remains vulnerable to concurrency issues. Can anyone please provide a scenario where syn开发者_Python百科chronized blocks/methods fail ? And, what should be the alternative in these cases to ensure thread safety.
Proper behaviour under concurrent access is a complex topic, and it's not as simple as just slapping synchronized
on everything, as now you have to think about how operations might interleave.
For instance, imagine you have a class like a list, and you want to make it threadsafe. So you make all the methods synchronized and continue. Chances are, clients might be using your list in the following way:
int index = ...; // this gets set somewhere, maybe passed in as an argument
// Check that the list has enough elements for this call to make sense
if (list.size() > index)
{
return list.get(index);
}
else
{
return DEFAULT_VALUE;
}
In a single-threaded environment this code is perfectly safe. However, if the list is being accessed (and possibly modified) concurrently, it's possible for the list's size to change after the call to size()
, but before the call to get()
. So the list could "impossibly" throw an IndexOutOfBoundsException (or similar) in this case, even though the size was checked beforehand.
There's no shortcut of how to fix this - you simply need to think carefully about the use-cases for your class/interface, and ensure that you can actually guarantee them when interleaved with any other valid operations. Often this might require some additional complexity, or simply more specifics in the documentation. If the hypothetical list class specified that it always synchronized on its own monitor, than that specific situation could be fixed as
synchronized(list)
{
if (list.size() > index)
{
return list.get(index);
}
}
but under other synchronization schemes, this would not work. Or it might be too much of a bottleneck. Or forcing the clients to make the multiple calls within the same lexical scope may be an unacceptable constraint. It all depends on what you're trying to achieve, as to how you can make your interface safe, performant and elegant.
Scenario 1 Classic deadlock:
Object Mutex1;
Object Mutex2;
public void method1(){
synchronized(Mutex1){
synchronized(Mutex2){
}
}
}
public void method2(){
synchronized(Mutex2){
synchronized(Mutex1){
}
}
}
Other scenarios include anything with a shared resource even a variable, because one thread could change the variables contents, or even make it point to null without the other thread knowing. Writing to IO has similar issues try writing code to a file using two threads or out to a sockeet.
Very good articles about concurrency and the Java Memory Model can be found at Angelika Langers website
"vulnerable to concurrency issues" is very vague. It would help to know what you have actually read and where. Two things that come to mind:
- Just slapping on "synchronized" somewhere does not mean the code is synchronized correctly - it can be very hard to do correctly, and developers frequently miss some problematic scenarios even when they think they're doing it right.
- Even if the synchronization correctly prevents non-deterministic changes to the data, you can still run into deadlocks.
Synchronized methods prevent other methods/blocks requiring same monitor from being executed when you execute them. But if you have 2 methods, lets say int get() and set(int val) and have somewhere else method which does obj.set(1+obj.get());
and this method runs in two threads, you can end with value increased by one or by two, depending on unpredictable factors.
Therefore you must somehow protect using such methods too (but only if its needed).
btw. use each monitor for as few functions/blocks as possible, so only those who can wrongly influence each other are synchronized.
And try to expose as few as possible methods requiring further protection.
精彩评论