Java 同步并发生在之前

Java synchronized and happens before

一个同步语句建立了一个happens-before 关系。但我不确定细节。 在 http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html 中可以阅读

An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor

我想知道我是否理解正确。因此,请看下面的示例。 假设有 2 个线程 T1、T2 共享 class 数据的相同实例数据和 class 对象的对象。 现在以下代码在给定的线程和顺序中执行:

(1)T1:  data.setValue("newValue");
(2)T1:  synchronized(object){}

(3)T2:  synchronized(object){}
(4)T2:  String str=data.getValue();

因为(1)和(2)是在同一个线程中执行的,所以一个有hb(1,2)和模拟hb(3,4)。在 (2) 中是监视器的解锁,在 (3) 中是同一监视器的锁定,因此是 hb(2,3),因此 hb(1,4) 和 str 应该等于 "newValue"。那是对的吗?如果不是 hb(2,3) 应该是错误的,但为什么?

编辑

因为class的详细资料需要回答问题:

public class Data {

private String value

 public void setValue(String newValue){
     value=newValue;
 }

 public String getValue getValue(){
     return value;
 }
}

编辑 2 很明显,不能保证执行顺序。当一个人有

(1*)T1:  synchronized(object){data.setValue("newValue");}

(2*)T2:  synchronized(object){String str=data.getValue();}

也不能保证 (1*) 在 (2*) 之前执行,但是如果我是对的,那么可以保证在 (2*) 之后有 str= "newValue" if (1 *) 在 (2*) 之前执行。我想知道第一个例子是否也是如此

没有。语句 2 不一定总是在语句 3 之前执行或发生。线程 2 可能会获取对象上的监视器,因此语句 3 将在语句 2 之前发生。

您无法控制哪个线程将实际获得 Object 的监视器,您也无法预测。

没那么简单。它还取决于 data.setValuedata.getValue 实际上在幕后做了什么。这些方法对于并发(非同步)调用安全吗? 在一个人为的示例中,如果数据由 HashMap 支持并且多个线程同时调用各种设置方法,则它可以 lead to an infinite loop.

总之,你只能保证执行顺序。您对 set 和 get 之间的内存可见性有一些有限的保证,但对 set 或 get 的并发调用没有任何潜在的副作用。

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?

是的,您的逻辑对于这个特定场景是正确的。如果(且仅当)23 之前执行,则 hb(2, 3)。要理解为什么会这样,请想象一个如下所示的线程过程:

localState *= 2;
synchronized(object) {
    sharedState = localState;
}

虽然 localState 是在 外部 同步块中计算的,但其他线程应该有必要将此计算看到 also 查看 sharedState.

的正确值

但是,重要的是要了解没有理由期望您所询问的订单作为结果。例如,它可能很容易以这种方式执行:

(1)T1:  data.setValue("newValue");

(3)T2:  synchronized(object){}
(4)T2:  String str=data.getValue();

(2)T1:  synchronized(object){}

这很糟糕,因为现在 T1 正在不同步地写入内存中的某个位置,而 T2 正要读取它。 (T2 甚至可以在写入的同时进行读取!)


要了解之前发生的事情,请想象这些线程 运行 并发(就像线程一样)并在以下时间线下执行:

  |            T1              |              T2
-------------------------------------------------------------
1 | synchronized(object){}     |
2 | data.setValue("newValue"); | String str=data.getValue();
3 |                            | synchronized(object){}

请注意我是如何调整这些假设行为的。

  • 1 点,T1 获取并释放锁。
  • 在点 2T1 执行写入,同时 T2 执行读取。
  • 3 点,T2 获取并释放锁。

但哪个 实际上首先发生 2 点? T1 的写入或 T2 的读取?

同步并不能保证线程相对于彼此实际执行的顺序。相反,它是关于线程之间的内存一致性

2点,因为没有同步,即使T1实际上使得写before T2读,T2可以自由查看内存中的old值。因此可以出现 T2(2)发生在T1(2)之前。

从技术上讲,这意味着在同步之外,线程可以在 CPU 缓存而不是主内存中自由 read/write。同步强制 read/write 在主内存中。

现在有第二个并发时间线:

             T1              |              T2
------------------------------------------------------------
 synchronized(object){       | synchronized(object){
  data.setValue("newValue"); |  String str=data.getValue();
 }                           | }

虽然我们不能保证哪个线程先获得锁,但是我们可以保证内存访问是一致的。我们还保证他们的行动不会重叠,这在第一个时间线中是可能的。

  • 如果T1先获得锁,保证T1的同步动作看起来好像发生在T2的动作之前。 (T1 肯定会在 T2 读取之前写入。)
  • 如果T2先获得锁,保证T2的同步动作看起来好像发生在T1的动作之前。 (T1一定会在T2读完后写。)