为了账号安全,请及时绑定邮箱和手机立即绑定

与Java中的易失性字段和同步块之间发生过关系-以及它们对非易失性变量的影响?

与Java中的易失性字段和同步块之间发生过关系-以及它们对非易失性变量的影响?

忽然笑 2019-12-25 11:21:26
我对线程的概念还很陌生,并尝试进一步了解它。最近,我遇到了Jeremy Manson撰写的关于Java中的Volatile Means的博客文章,他在文章中写道:当一个线程写一个易失性变量,而另一个线程看到该写操作时,第一个线程向第二个线程告知内存的所有内容,直到它执行对该易失性变量的写操作为止。[...] 所有的由线程1看到的存储内容,才写信给[volatile] ready,必须是可见的主题2,它读取值后true进行ready。[我自己加重]现在,这是否意味着在写入volatile变量时线程1的内存中保存的所有变量(无论是否为volatile)在读取该volatile变量后对线程2都是可见的?如果是这样,是否有可能从Java官方文档/ Oracle来源中一起混淆该声明?从哪个Java版本开始可以使用?特别是,如果所有线程共享以下类变量:private String s = "running";private volatile boolean b = false;线程1首先执行以下命令:s = "done";b = true;然后线程2随后执行(在线程1写入volatile字段之后):boolean flag = b; //read from volatileSystem.out.println(s);是否可以保证打印“完成”?如果b在volatile我将写入和读取放入一个synchronized块时没有声明,该怎么办?此外,在名为“ 线程之间共享静态变量吗? ”的讨论中,@ TREE 写道:不要使用volatile来保护多个共享状态。为什么?(抱歉;我无法在其他问题上发表评论,否则我会在那儿问...)
查看完整描述

3 回答

?
临摹微笑

TA贡献1982条经验 获得超2个赞

是的,可以保证线程2将打印“ done”。当然,也就是说,如果b线程1中的写入实际上发生b在线程2中的读取之前,而不是同时发生或更早!

推理的核心是事前发生的关系。多线程程序执行被视为由事件组成。事件可以通过事前发生关系来关联,即一个事件先于另一个事件发生。即使两个事件没有直接关联,如果您可以跟踪从一个事件到另一个事件的一系列先发生后关系,那么您可以说一个事件先于另一个事件发生。

就您而言,您有以下事件:

  • 线程1写入 s

  • 线程1写入 b

  • 线程2从读取 b

  • 线程2从读取 s

以下规则起作用:

  • “如果x和y是同一线程的动作,并且x按程序顺序位于y之前,则hb(x,y)。” (程序顺序规则)

  • “在每次对该字段进行后续读取之前,都会对易失字段(第8.3.1.4节)进行写操作。” (波动性规则)

因此,存在以下先于关系:

  • 线程1写入s发生在线程1写入b之前(程序顺序规则)

  • 线程1写入b发生在线程2从b(易失性规则)读取之前发生

  • 线程2读取b发生在线程2读取s之前(程序顺序规则)

如果您遵循该链,则可以看到以下结果:

  • 线程1写入s发生在从线程2读取之前s


查看完整回答
反对 回复 2019-12-25
?
缥缈止盈

TA贡献2041条经验 获得超4个赞

是否可以保证打印“完成”?

如Java并发实践中所述:

当线程A写入到一个volatile变量,然后线程B读取相同的变量,那名至A之前写可见的所有变量的值volatile读取后变量变得可见到B volatile变量

所以是的,这保证打印“完成”。

如果不是将b声明为volatile而是将写入和读取放入同步块,会发生什么情况?

这也将保证相同。

不要使用volatile来保护多个共享状态。

为什么?

因为,volatile仅保证可见性。它不能保证原子性。如果我们在一个线程正在访问的方法中有两个易失性写操作,A而另一个线程B正在访问这些易失性变量,则在线程A执行该方法时,线程有可能在操作中间A被线程抢占B(例如在第一次volatile写入之后但在第二次volatile写入之前A)因此,保证操作的原子性synchronization是最可行的出路。


查看完整回答
反对 回复 2019-12-25
  • 3 回答
  • 0 关注
  • 332 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信