Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/381.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在同步方法退出之前发生_Java_Multithreading - Fatal编程技术网

Java 在同步方法退出之前发生

Java 在同步方法退出之前发生,java,multithreading,Java,Multithreading,甲骨文声明: 其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立“发生在之前”关系 这似乎非常正确,因为“后续”意味着它发生在同步方法退出之后。这种说法似乎也适用于不同步的方法。我错过了什么?你错过了“后续”与时间的流逝无关;它只与方法调用在同步顺序中出现的位置有关。这是线性化(严格按挂钟时间排序,Java内存模型不保证)和顺序一致性(与某些排序一致,为无数据竞争的程序保证)之间的本质区别。“发生在”这个短语也具有欺骗性,因为它也不意味着时间顺序 对于未同步的方法,根本没有顺

甲骨文声明:

其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立“发生在之前”关系

这似乎非常正确,因为“后续”意味着它发生在同步方法退出之后。这种说法似乎也适用于不同步的方法。我错过了什么?

你错过了“后续”与时间的流逝无关;它只与方法调用在同步顺序中出现的位置有关。这是线性化(严格按挂钟时间排序,Java内存模型不保证)和顺序一致性(与某些排序一致,为无数据竞争的程序保证)之间的本质区别。“发生在”这个短语也具有欺骗性,因为它也不意味着时间顺序

对于未同步的方法,根本没有顺序,因为它们的调用不按同步顺序出现

正如您在评论中很好地总结的:

每个执行都有一些同步操作的顺序,这些顺序可能与“实际”执行顺序相同,也可能不同。但是,此同步顺序与各个线程的程序顺序一致。此外,如果在同步顺序中退出同步方法的时间早于在同一对象上输入同步方法的时间,则输入方法中的所有读取操作都将参见退出方法中的写入操作


假设有一个非易失性字段
f
保存值v,假设线程a使用值w更新字段
f
,并假设线程B随后读取字段
f

您可能会惊讶地发现,在更新和读取之间没有任何before关系。Java语言规范(JLS)允许线程B在读取字段时获取v,即使根据挂钟,读取发生在更新之后

在一个线程中发生的所有事情都需要按程序顺序发生。如果线程A执行的代码看起来像是更新了某个字段,然后又读取了该字段,那么JLS要求读取操作获取已写入的值(即,写入操作“发生在”读取操作之前)

当读和写发生在不同的线程中时,没有这样的要求。一般来说,一个线程中发生的事件独立于另一个线程中发生的事件,某些特殊情况除外。进入和离开
synchronized
块就是这些特殊情况之一


当JLS说离开同步块“发生在”同一对象上同步块的后续条目之前,这意味着即使事件发生在不同的线程中,发生之前关系也为真。

同步顺序和发生之前关系之间有什么区别?发生之前包含的动作比同步顺序多,因为发生之前也包含非同步动作的程序顺序。请注意不要假设“以前发生过”与时间的流逝有关,因为它与时间无关。这只是关于因果关系排序。
A与B同步
意味着
A发生在B
之前。非同步方法不会出现在同步顺序中,因此除非在同一线程上调用它们,否则它们不会获得排序(按程序顺序排序)。但是,
A在同步顺序B的前面
并不意味着
A与B同步。只有涉及同一个锁对象的操作才能得到它,然后只有一对操作,其中A是释放操作,B是获取操作。这对于易失性字段和非易失性字段都是如此,因为时间在JMM中根本不重要,或者在一般的顺序一致性中也不重要。@MarkoTopolnik,时间之所以重要,是因为时间发生了。事件实际上是按一定的时间顺序发生的。有时我们可以对订单进行推理,有时则不能。如果我的示例中的线程B读取字段
f
,并获得值w,则证明线程A在线程B读取字段之前编写了字段。我想告诉OP的是,“x发生在y之前”并不意味着x总是发生在y之前。这只意味着,如果你能证明x确实发生在y之前,那么你就可以利用这些信息来推断其他相关事件的时间顺序。“事件实际上是按一定的时间顺序发生的”——即使这不是真的。只要遵守规范的规则,就可能没有与易失性写入相对应的实际事件。观察一个值并不能证明机器上的物理事件。例如,在写入值之前,可以对其进行推测性观察。你绝对不允许对时间排序进行推论,因为这意味着线性化,这是不能保证的。作为一个具体的例子,考虑一下这个。最初是一个可变变量
x==0
。线程a在某个点将1写入
x
。在时间
t==1
时,线程b观察到
x==1
。在时间
t==2
时,线程c观察到
x==0
。这违反了JMM吗?@MarkoTopolnik,我看没问题。但让我更进一步:让线程a在设置
x=1
之前设置非易失性
y=1
,线程b和c在检查
x
之后检查
y
。我的理解是,JLS要求线程b看到
y==1
,如果它以前看到
x==1
,但是线程c可以看到
y==0
或者
y==1
,如果它以前看到
x==0
。线程b可以完成“之前发生”的推断链,因为它可以看到易失性写入发生了。线程c无法完成该链,因为尚未收到有关易失性写入的消息。