如何解决变量应该在 2 个正在运行的线程之间是 volatile 吗?
在这种情况下,int a
应该是 volatile 以保证线程之间的可见性吗?
private volatile static int a = 0;
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
a = 10;
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(a);
}
});
t1.start();
t2.start();
}
10
解决方法
happens-before is clearly defined in the language specification,从阅读开始;开始。
然后要完全了解正在发生的事情,您需要知道 Program order 是什么,以及 synchronization order。
简单来说,看下面:
private volatile static int a = 0;
private static int b = 0;
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
b = 100;
a = 10;
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
if(a == 10){
System.out.println(b);
}
}
});
t1.start();
t2.start();
}
您唯一可以保证的是,当且仅当 t2
打印某些内容时,它始终为 100
。这是因为 t2
已经看到一个 volatile write
到 a
。发生这种情况是因为从写入线程到读取线程已经建立了“happens-before”,并且在 a = 10
之前完成的每个操作都保证对已经看到 a
的线程可见10
。
你能否进一步解释一下“happens-before”?
关于“发生在之前”要记住的最重要的事情是它是一个 transitive relation。这意味着,如果 Java 语言规范 (JLS) 承诺 A “发生在” B 之前,并且承诺 B “发生在” C 之前,那么您可以推断出 A “发生在” C 之前的承诺。
JLS 表示对某个 volatile
变量的写入“发生在”同一变量的后续读取之前。
好吧!听起来很明显,不是吗?
但它不很明显,因为 JLS 没有不为非易失性变量提供相同的保证。如果处理器 A 将值 7 写入非易失性 int,然后一段时间后处理器 B 写入 5,则 JLS 不保证一段时间后,变量的最终值将是 5。处理器 B 将看到 5 (这是一个不同的“发生在之前”的承诺,见下文)。处理器 A 可以看到 5 或 7,任何其他处理器都可以看到 5 或 7 或变量最初具有的任何值(例如 0)。
volatile
承诺如何提供帮助
假设我们有
volatile boolean flag = false;
/*non-volatile*/ int i = 0;
假设线程 A 这样做:
i = 7;
flag = true;
假设线程 B 这样做:
if (flag) {
System.out.println(i);
}
else {
System.out.println("Bleah!");
}
线程 B 可以打印“7”,也可以打印“Bleah!”但是由于“发生在之前”的保证,我们绝对知道线程 B 永远不会打印“0”。为什么不呢?
线程 A 在设置 i = 7
之前设置了 flag = true
。 JLS 保证如果单个线程在执行第二个语句之前执行一个语句,则第一个语句“发生在”第二个语句之前。 (这听起来非常明显,但同样,它不应该。很多与线程有关的事情不明显。)
线程 B 在打印 flag
之前测试 i
。所以 *IF* 线程 A 之前设置了 flag=true
那么我们知道 i
必须等于 7
:传递性:i=7
“发生在之前”{{ 1}},以及对 flag=true
的写入,IF IT IT HAPPENED AT ALL,“发生在”同一 volatile flag
的读取之前。
如果真的发生了
数据竞争和竞争条件
要记住的最重要的事情是,当 JLS 承诺 A“在 B 之前发生”时,他们并不是说 A 确实总是在 B 之前发生:他们是说您可以依赖这种传递关系。他们说如果 A 确实发生在 B 之前,那么所有“发生在 A 之前”的事情也一定确实发生在 B 之前。
程序可以打印“Bleah!”因为没有什么可以阻止线程 B 在线程 A 设置它之前测试 flag
。有些人称之为“数据竞赛”。这两个线程正在“竞赛”,看哪个线程先到达 flag
,程序的结果取决于哪个线程在这场竞赛中获胜。
当程序的正确性取决于数据竞争的结果时,我们中的一些人将其称为“竞争条件”,这确实令人头疼。无法保证具有竞争条件的程序不会在您的测试期间做一千次正确的事情,然后在对您的客户最重要的时候做错误的事情。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。