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.setValue
和 data.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?
是的,您的逻辑对于这个特定场景是正确的。如果(且仅当)2
在 3
之前执行,则 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
获取并释放锁。
- 在点
2
,T1
执行写入,同时 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
读完后写。)
一个同步语句建立了一个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.setValue
和 data.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?
是的,您的逻辑对于这个特定场景是正确的。如果(且仅当)2
在 3
之前执行,则 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
获取并释放锁。 - 在点
2
,T1
执行写入,同时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
读完后写。)