Java synchronized and happens before [duplicate]

十年热恋 提交于 2019-12-02 10:14:01

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 simulaneously T2 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 that T1's synchronized actions will appear as if happening before T2's actions. (T1 will definitely write before T2 reads.)
  • If T2 acquires the lock first, it is guaranteed that T2's synchronized actions will appear as if happening before T1's actions. (T1 will definitely write after T2 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.

Brett Okken

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!