在 Java 中与易失性字段和同步块的关系发生之前 - 以及它们对非易失性变量的影响? [英] Happens-before relationships with volatile fields and synchronized blocks in Java - and their impact on non-volatile variables?

查看:21
本文介绍了在 Java 中与易失性字段和同步块的关系发生之前 - 以及它们对非易失性变量的影响?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对线程的概念还很陌生,并尝试更多地了解它.最近,我看到了一篇关于 What Volatile 的博文Means in Java 作者:Jeremy Manson,他写道:

I am still pretty new to the concept of threading, and try to understand more about it. Recently, I came across a blog post on What Volatile Means in Java by Jeremy Manson, where he writes:

当一个线程写入一个 volatile 变量,而另一个线程看到那个写,第一个线程告诉第二个关于所有内存的内容,直到它执行写入该易失性多变的.[...] 所有 线程 1 看到的内存内容,之前它写入 [volatile] ready,必须对线程 2 可见,在它之后ready 读取值 true.[重点是自己加的]

When one thread writes to a volatile variable, and another thread sees that write, the first thread is telling the second about all of the contents of memory up until it performed the write to that volatile variable. [...] all of the memory contents seen by Thread 1, before it wrote to [volatile] ready, must be visible to Thread 2, after it reads the value true for ready. [emphasis added by myself]

现在,这是否意味着在写入 volatile 变量时线程 1 的内存中保存的所有变量(无论是否为 volatile)都将在线程 2 读取该 volatile 变量后可见?如果是这样,是否有可能从官方 Java 文档/Oracle 资源中将该语句拼凑起来?从哪个版本的 Java 开始,这会起作用?

Now, does that mean that all variables (volatile or not) held in Thread 1's memory at the time of the write to the volatile variable will become visible to Thread 2 after it reads that volatile variable? If so, is it possible to puzzle that statement together from the official Java documentation/Oracle sources? And from which version of Java onwards will this work?

特别是,如果所有线程共享以下类变量:

In particular, if all Threads share the following class variables:

private String s = "running";
private volatile boolean b = false;

线程 1 首先执行以下操作:

And Thread 1 executes the following first:

s = "done";
b = true;

然后线程 2 然后执行(在线程 1 写入 volatile 字段之后):

And Thread 2 then executes afterwards (after Thread 1 wrote to the volatile field):

boolean flag = b; //read from volatile
System.out.println(s);

这能保证打印done"吗?

Would this be guaranteed to print "done"?

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

What would happen if instead of declaring b as volatile I put the write and read into a synchronized block?

此外,在题为线程之间是否共享静态变量?", @TREE :

Additionally, in a discussion entitled "Are static variables shared between threads?", @TREE writes:

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

Don't use volatile to protect more than one piece of shared state.

为什么?(抱歉;我还不能评论其他问题,否则我会在那里问...)

Why? (Sorry; I can't comment yet on other questions, or I would have asked there...)

推荐答案

是的,保证线程 2 会打印done".当然,这是如果在线程 1 中写入 b 实际上发生在线程 2 中从 b 读取之前,而不是同时发生,或更早发生!

Yes, it is guaranteed that thread 2 will print "done" . Of course, that is if the write to b in Thread 1 actually happens before the read from b in Thread 2, rather than happening at the same time, or earlier!

这里推理的核心是发生在关系之前.多线程程序执行被视为由事件组成.事件可以通过happens-before 关系相关联,即一个事件先于另一个事件发生.即使两个事件没有直接关系,如果您可以追踪从一个事件到另一个事件的一系列先发生关系,那么您就可以说一个发生在另一个之前.

The heart of the reasoning here is the happens-before relationship. Multithreaded program executions are seen as being made of events. Events can be related by happens-before relationships, which say that one event happens before another. Even if two events are not directly related, if you can trace a chain of happens-before relationships from one event to another, then you can say that one happens before the other.

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

In your case, you have the following events:

  • 线程 1 写入 s
  • 线程 1 写入 b
  • 线程 2 从 b
  • 读取
  • 线程 2 从 s
  • 读取

以下规则开始起作用:

  • 如果 x 和 y 是同一个线程的动作,并且 x 在程序顺序中排在 y 之前,那么 hb(x, y)."(程序顺序规则)
  • 写入易失性字段(第 8.3.1.4 节)发生在每次后续读取该字段之前."(volatile 规则)
  • "If x and y are actions of the same thread and x comes before y in program order, then hb(x, y)." (the program order rule)
  • "A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field." (the volatile rule)

因此存在以下发生在关系之前:

The following happens-before relationships therefore exist:

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

如果你遵循那个链,你可以看到结果:

If you follow that chain, you can see that as a result:

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

这篇关于在 Java 中与易失性字段和同步块的关系发生之前 - 以及它们对非易失性变量的影响?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆