java监视器的移除_Java-监视器解锁之前发生的关系

The rule for a monitorexit (i.e., releasing synchronization) is that

actions before the monitorexit must be performed before the monitor is released.

对我来说似乎很明显,但是在定义之前已阅读了http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html,并且在发生之前,我所能发现的有关监视器解除阻塞的所有信息是,当一个线程解锁发生的监视器时-在另一个线程再次锁定它之前(这也很有意义).有人可以解释一下JLS如何解释在解锁操作之前必须执行同步块中所有动作的明显情况吗?

进一步的评论:

基于几个回复,我想对你们一直在说的话写一些进一步的评论:

>在单线程内重新编排

我引用了几个“真相”:

a = new A()

如果新的A()涉及一百个操作,然后将堆上的地址分配给a,则编译器可以简单地重新排序,以将堆地址分配给a,然后遵循通常的初始化(双重检查锁定的问题)

synchronized{

a = 5;

}

print a;

可以更改为

synchronized{

a = 5;

print a;

}

因此,我们使用打印语句对monitorexit进行了重新排序(根据JLS也有效)

现在,我提到的简单案例:

x = 1;

y = 2;

c = x + y;

print c;

我没有理由阻止编译器先分配x或y.完全没有什么可以停止它的,因为无论x是首先分配还是y是分配,最终输出都不会改变.因此重新排序是完全可能的.

> monitor.unlock

根据将print语句“拉入”同步块的示例,让我们尝试扭转这种情况,即使用

synchronized{

a = 5;

print a;

}

我可以期望编译器执行此操作:

synchronized{

a = 5;

}

print a;

在单线程世界中看起来是完全合理的,但是对于JLS(根据引用的来源),这绝对是无效的.现在,如果在JLS中找不到关于此的任何内容,为什么会这样呢?显然,关于“程序顺序”的动机现在已经不相关了,因为编译器可以进行重新编译,例如将语句“拉入”到同步块中.

解决方法:

这不仅是在同步块内执行的所有动作,还指的是MonitorExit之前该线程的所有动作.

Could someone explain how JLS explains the obvious condition that all

the actions within the synchronization block must happen-before the

unlock operation?

对于一个特定的线程(只有一个线程),所有操作(无论是否同步)都保持程序顺序,因此看起来所有读取和写入都是按顺序进行的(在单线程情况下,在顺序之前我们不需要发生操作).

事前发生关系考虑了多个线程,也就是说,在连续的monitorenter之后,monitorexit之前在一个线程中发生的所有操作对于所有线程都是可见的.

编辑以解决您的更新.

编译器必须遵循某些特定规则才能重新排序.在这种情况下,特定的一种在here的“可重新订购”网格中得到了证明.

特别有用的条目是

>第一步:正常加载(加载a;打印a)

>第二步:监控退出

这里的值为No表示编译器无法对两个操作进行重新排序,其中第一个是正常加载,第二个是monitorexit,因此在您的情况下,此重新排序将违反JLS.

有一种规则称为蟑螂汽车旅馆排序,即可以将读/写重新排序为同步块,但不能重新排序.

标签:multithreading,happens-before,java,memory-model,jls

来源: https://codeday.me/bug/20191122/2057026.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值