Memory effects of synchronized keyword in Java

Deadly 提交于 2019-12-21 18:41:05

问题


This might have been answered before, but because of the complexity of the issue, I need a confirmation. So I rephrase the question

Question 1 : When a thread enters a synchronized block, the memory barrier will include any fields touched, not just fields of the object that I synchronized on? So if many many objects are modified inside a synchronized block, that's a lot of memory moves between thread memory caches.

Thread 1
object.field1 = "";
synchronized (lock) {
  farAwayObject.field1 = "";
  farAwayObject.evenFarther.field2 = "";
}

Thread 2. assuming thread ordering is correct
synchronized (lock) {
  //thread 2 guaranteed to see all fields above as ""
  //even object.field1 ?
}

Question 2 : Is object.field1 = ""; in thread 1 implicitly part of the happens-before relationship?

I hope it is but It might not. If not is there a trick to make it so without putting it into the sync block? It is hard to reason on the program otherwise and it is not practical to put everything under the synchronized { }.

EDIT: clarification: object.field1 is not volatile and the question is "will thread 2 guaranteed to see the write of thread 1, at least ". My question is about memory visibility. For the sake of the argument, let's say only thread 1 writes to non volatile object.field1.

The question 2 can be rephrased as

"Will a synchronized block on a lock push changes made before to be seen by other threads synchronizing on the same lock? "


回答1:


When a thread enters a synchronized block, the memory barrier will include any fields touched, not just fields of the object that I synchronized on

Assuming that the fields of farAwayObject and evenFarther are always modified and accesed by obtaining a lock on the same object all around your application, all threads will always see the updates made to farAwayObject and evenFarther since synchronized enforces a happens-before condition.

//thread 2 guaranteed to see all fields above as ""

The same cannot be said for object.field1 without knowing how it was declared. Assuming that field1 is a reference not marked as volatile, it will not be a part of the happens before relationship and threads may see stale values for it.

I hope it is but It might not. If not is there a trick to make it so without putting it into the sync block?

Yes. Mark object.field1 as volatile.


Addresing your edit :

The question 2 can be rephrased as

"Will a synchronized block on a lock push changes made before to be seen by other threads synchronizing on the same lock? "

AFAIK the answer is Yes, provided that the writing threads acquire the lock before the reading threads. Unfortunately, this is something you generally can't guarantee and that's why object.field1 needs to be marked as volatilethe statement needs to moved inside the synchronized block.

Take a look at JSR 133 which talks about the cache being flushed to main memory when the thread exits a syncronized block. This should clarify things further.





回答2:


1) When a thread enters a synchronized block, the memory barrier will include any fields touched, not just fields of the object that I synchronized on?

Correct. (Assuming that thread 1 and thread 2 synchronize on the same lock.)

So if many many objects are modified inside a synchronized block, that's a lot of memory moves between thread memory caches.

Potentially, yes. However, it is (probably) not movement between caches. More likely, it is a movement from one processor's cache to memory, and from memory to a second processor's cache. Of course, that depends on how the hardware implements the memory hierarchy.

2) Is object.field1 = ""; in thread 1 implicitly part of the happens-before relationship?

There is a chain of happens-before relations

  1. The write to object.field1 happens before the lock is acquired.
  2. That happens before the writes to farAwayObject and so on.
  3. That happens before the lock is released by thread 1
  4. That happens before the lock is acquired by thread 2
  5. That happens before thread 2 reads object.field1.

The problem is what happens if there is an intervening write to object.field1, either before the lock is acquired by thread 1, or by some other thread. In either of those cases, the happens-before chain is not sufficient to ensure that thread 2 sees the value that was written by thread 1.




回答3:


the memory barrier will include any fields touched, not just fields of the object that I synchronized on?

Yes.

Is object.field1 = ""; in thread 1 implicitly part of the happens-before relationship?

Yes, even if it is not volatile.


The happens-before order is a partial order.

The happens-before order is given by the transitive closure of synchronizes-with edges and program order. It must be a valid partial order: reflexive, transitive and antisymmetric.

(JLS 17.4.7)

Actions before a synchronization edge (i.e. The release of the synchronized lock) are ordered by program order and thus synchronized with the release. Transitivity says that actions that are ordered by an acquire of the same lock in another thread therefore has a happens-before order with both the release of that lock AND the actions preceding the release of that lock, whether or not it is inside the body of the synchronized block. The important thing to remember about this is that ordering occurs on actions (i.e. Acquire/release of a lock) not a block such as implied by the brackets of the synchronized keyword. The brackets indicate the position of the acquire/release actions as well as where a set of actions cannot be interleaved.

Finally, remember that happens-before is a "partial" order. It means:

  1. Happens before enforces memory consistency when actions happen to come in the particular order (i.e. release/acquire, write/read, etc)
  2. Happens before depends on stronger guarantees such as program order to produce the correct functionality.
  3. Happens before does not prevent errors coming from interleaving of nonatomic actions
  4. Happens before is the transitive relationship between the action and a strong action. (You can put the read of your shared variable outside the synchronized block as long as the write comes before the block and the read comes after the block). Stronger ordering guarantees also follow happens before ordering, but they also provide additional effects described in the spec.


来源:https://stackoverflow.com/questions/42107083/memory-effects-of-synchronized-keyword-in-java

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