Sync to non-target

private volatile Object obj = new MyObject(); void foo() { synchronized(obj) { obj.doWork(); } } void bar() { synchronized(obj) { obj.doWork(); obj = new MyObject(); // <<<< notice this line (call it line-x) } } 

Suppose that at a certain point in time, t_bar executes bar() and another t_foo executes foo and that t_bar just received obj , so t_foo is essentially waiting.

After executing the synchronization block in bar , foo will be able to execute its synchronization block, right? What value will obj see? Old? Or a new one set to bar ?

(I would hope that we see a new meaning, that the whole point of its coding, but I want to know if this is a "safe" bet)

+7
source share
5 answers

In the exact situation that you described, yes, reading obj inside the foo synchronized block will see the new value set by the previous synchronized block.

The most interesting thing is that this does not always happen in this exact situation. The program is not thread safe, for example, if immediately after bar() exits, the same threads start another bar() , while foo blocks the old object. The bar thread blocks a new object, so two threads are executed simultaneously, both are executed by obj.doWork() on the same new object.

Perhaps we will partially fix it

 // suppose this line happens-before foo()/bar() calls MyObject obj = new MyObject(); void foo() while(true) MyObject tmp1 = obj; synchronized(tmp1) MyObject tmp2 = obj; if(tmp2==tmp1) tmp2.doWork(); return; // else retry 

this at least ensures that there are no current calls to obj.doWork() on the same object, since obj.doWork() can only be executed in a synchronized block that blocks the same obj

+2
source

It will behave normally, as if the reference to the object was not changed internally. The reason is that the object lock check will be performed only once. Thus, even if the object changes internally, the flow will continue to wait, and the behavior will be reproduced as if the object were the same [unchanged].

I tried another. I placed the sleep operator right after creating a new object, and then started the next thread and, as expected, both threads started working simultaneously. See code below.

 public class ChangeLockObjectState { private volatile Object obj = new Object(); void foo() { synchronized (obj) { try { System.out.println("inside foo"); Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } void bar() { synchronized (obj) { try { System.out.println("inside bar"); Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } obj = new Object(); // <<<< notice this line (call it line-x) System.out.println("going out of bar"); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("wait over"); } } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { final ChangeLockObjectState test = new ChangeLockObjectState(); new Thread(new Runnable() { @Override public void run() { test.bar(); } }).start(); Thread.sleep(6000); new Thread(new Runnable() { @Override public void run() { test.foo(); } }).start(); } } 
+1
source

It is unsafe and broken. Changing a locked object does not work.

When a thread tries to enter a synchronized block, it must first evaluate the expression in parens to find out what kind of lock it needs. If after this the lock changes, the thread does not know about it, it ultimately receives the old lock and enters the synchronized block. At this moment, he sees the object and evaluates it, getting a new link and calls the method on it with the old (now irrelevant) lock and not holding the new lock, even if some other thread can have a new lock and can simultaneously execute the method on the same object.

0
source

The new value is displayed. And it works even without creating obj volatile . This is due to the fact that synchronization is still stored on the old object and ensures the visibility of the new value after the wait stream (t_foo) gets inside. Here is the test:

 public class Main3 { private MyObject obj = new MyObject(1); void foo() { synchronized(obj) { System.out.println(obj.number); obj.doWork(); } } void bar() { synchronized(obj) { System.out.println(obj.number); obj.doWork(); //force the foo thread to wait at the synchronization point for(int i = 0; i < 1000000000l; i++); obj = new MyObject(2); // <<<< notice this line (call it line-x) } } public static void main(String[] args) throws InterruptedException { final Main3 m3 = new Main3(); Thread t1 = new Thread( new Runnable() { @Override public void run() { m3.bar(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { m3.foo(); } }); t1.start(); t2.start(); } } class MyObject { int number; public MyObject(int number) { this.number = number; } public void doWork() { } } 
0
source

The new obj value will be read.

From the standard section Happens to :

Writes to an unstable field (ยง8.3.1.4) - before each subsequent reading of this field.

From the definition of a shared variable:

All instance fields, static fields, and array elements are stored in heap memory. In this chapter, we use the term variable to refer to both fields and elements of the array. Local variables (ยง14.4), formal method parameters (ยง8.4.1), and exception handler parameters (ยง14.20) are never shared between threads and are independent of the memory model.

Reading obj inside the synchronized block is separate from the initial evaluation of the expression obj to determine which object in the built-in monitor is being locked. The reassignment of obj will occur before the first reading, but not the second. Since obj is a volatile field, the second read should see the updated value of obj .

-2
source

All Articles