This question already has an answer here:
A synchronized statement establishes a happens-before relation. But im not sure about the details. In http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html one can read
An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor
I want to know if i understood that correctly. Therefore have a look at the following example. Lets assume that there are 2 Threads T1,T2 sharing the same instance data of a class Data and object of the class Object. Now the following code gets executed in the given threads and order:
(1)T1: data.setValue("newValue");
(2)T1: synchronized(object){}
(3)T2: synchronized(object){}
(4)T2: String str=data.getValue();
because (1) and (2) are executed in the same thread, one has hb(1,2) and analogue hb(3,4). In (2) is an unlock of the monitor and in (3) a lock of the same monitor, thus hb(2,3), therefore hb(1,4) and str should be equal to "newValue". Is that correct? If not than hb(2,3) should be wrong, but why?
Edit
Because details of the class Data is needed to answer the question:
public class Data {
private String value
public void setValue(String newValue){
value=newValue;
}
public String getValue getValue(){
return value;
}
}
Edit 2 its clear, that one cannot guarantee the order of execution. When one has instead
(1*)T1: synchronized(object){data.setValue("newValue");}
(2*)T2: synchronized(object){String str=data.getValue();}
one has also no guarantee that (1*) is exectuted before (2*), but if im right, one has the guarantee that after (2*) one has str= "newValue" if (1*) was executed before (2*). I want to know if the same holds for the 1st example
because (1) and (2) are executed in the same thread, one has hb(1,2) and analogue hb(3,4). In (2) is an unlock of the monitor and in (3) a lock of the same monitor, thus hb(2,3), therefore hb(1,4) and str should be equal to "newValue". Is that correct?
Yes, your logic is correct for this specific scenario. If (and only if) 2
executes before 3
then hb(2, 3)
. To understand why it should be, imagine a thread process such as the following:
localState *= 2;
synchronized(object) {
sharedState = localState;
}
Although localState
is computed outside a synchronized block, it should be necessary for other threads to see this computation to also see the correct value for sharedState
.
However, it's important to understand that there is no reason to expect the order you've asked about as the outcome. For example it could just as easily happen to execute this way:
(1)T1: data.setValue("newValue"); (3)T2: synchronized(object){} (4)T2: String str=data.getValue(); (2)T1: synchronized(object){}
This is bad because now T1
is writing to a location in memory without synchronization while T2
is about to read it. (T2
could even read at the same time the write is occurring!)
To understand what happens-before is all about, instead imagine these threads are running concurrently (as threads do) and execute under the following timeline:
| T1 | T2 ------------------------------------------------------------- 1 | synchronized(object){} | 2 | data.setValue("newValue"); | String str=data.getValue(); 3 | | synchronized(object){}
Notice how I've aligned these hypothetical actions.
- At point
1
,T1
acquires the lock and releases it. - At point
2
,T1
executes a write while simulaneouslyT2
executes a read. - At point
3
,T2
acquires the lock and releases it.
But which actually happens first at point 2
? T1
's write or T2
's read?
Synchronization doesn't guarantee the order that threads actually execute with respect to one another. Instead, it is about memory consistency between threads.
At point 2
, because there is no synchronization, even if T1
actually makes the write before T2
reads it, T2
is free to see the old value in memory. Therefore it can appear that T2(2)
happened before T1(2)
.
Technically what this means is that outside of synchronization, a thread is free to read/write in a CPU cache instead of main memory. Synchronization forces the read/write in main memory.
Now with the second concurrent timeline:
T1 | T2 ------------------------------------------------------------ synchronized(object){ | synchronized(object){ data.setValue("newValue"); | String str=data.getValue(); } | }
Although we do not have a guarantee about which thread acquires the lock first, we do have a guarantee that the memory access will be consistent. We also have a guarantee that their actions will not overlap, which was possible in the first timeline.
- If
T1
acquires the lock first, it is guaranteed thatT1
's synchronized actions will appear as if happening beforeT2
's actions. (T1
will definitely write beforeT2
reads.) - If
T2
acquires the lock first, it is guaranteed thatT2
's synchronized actions will appear as if happening beforeT1
's actions. (T1
will definitely write afterT2
reads.)
No. It's not necessary that statement 2 will always execute or happen before statement 3. It can happen that thread 2 will acquire the monitor on object and hence statement 3 will happen before statement 2.
You don't have control over which thread will actually get the monitor of Object and you can't predict.
It is not quite that simple. It also depends on what data.setValue
and data.getValue
actually do under the covers. Are those methods safe for concurrent (unsynchronized) calls?
In one contrived example, if data were backed by a HashMap
and multiple threads call various set methods concurrently, it could lead to an infinite loop.
In short, you are only able to guarantee the order of execution. You have some limited guarantees to memory visibility between the set and get, but not concurrent calls to set or get having any potential side effects.
来源:https://stackoverflow.com/questions/28384617/java-synchronized-and-happens-before