变量应该在 2 个正在运行的线程之间是 volatile 吗? [英] Should a variable be volatile between 2 running threads?

查看:27
本文介绍了变量应该在 2 个正在运行的线程之间是 volatile 吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这种情况下,int a 是否应该是 volatile 以保证线程之间的可见性?

Should int a in this case be volatile to guarantee visibilty between threads?

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"?

Could you explain yourself a bit further on "happens-before"?

关于发生在之前"要记住的最重要的事情是一种传递关系.这意味着,如果 Java 语言规范 (JLS) 承诺 A 发生在之前"B,并且它承诺 B 发生在之前"C,那么您可以推断出 A发生在之前"的承诺;C.

The most important thing to remember about "happens before" is that it's a transitive relation. That means, if the Java Language Spec (JLS) promises that A "happens before" B, and it promises that B "happens before" C, then you can infer a promise that A "happens before" C.

JLS 表示写入某个 volatile 变量发生在"之前对同一变量的后续读取.

The JLS says that a write to some volatile variable "happens before" a subsequent read of the same variable.

好吧!听起来很明显,不是吗?

Well Duh! Sounds obvious, doesn't it?

但它很明显,因为 JLS 没有为非易失性变量提供相同的保证.如果处理器 A 将值 7 写入非易失性 int,然后一段时间后处理器 B 写入 5,则 JLS 不保证一段时间后,变量的最终值将是 5.处理器 B 将看到 5(这是一个不同的发生在"之前的承诺,见下文).处理器 A 可以看到 5 或 7,任何其他处理器都可以看到 5 或 7 或变量最初具有的任何值(例如 0).

But it's not obvious because the JLS does not give the same guarantee for a non-volatile variable. If processor A writes the value 7 to a non-volatile int, and then some time later processor B writes 5, the JLS does not guarantee that some long time later, the final value of the variable will be 5. Processor B will see 5 (that's a different "happens before" promise, see below). Processor A could see 5 or 7, and any other processor could see 5 or 7 or whatever value the variable had initially (e.g., 0).

volatile 承诺的作用

How the volatile promise helps

假设我们有

 volatile boolean flag = false;
 /*non-volatile*/ int i = 0;

假设线程 A 这样做:

Suppose thread A does this:

i = 7;
flag = true;

假设线程 B 这样做:

And suppose thread B does this:

if (flag) {
    System.out.println(i);
}
else {
    System.out.println("Bleah!");
}

线程 B 可以打印7",也可以打印Bleah!";但因为发生在之前"保证,我们绝对知道线程 B 永远不会打印0".为什么不呢?

Thread B could print "7", or it could print "Bleah!" but because of the "happens before" guarantee, we absolutely know that thread B will never print "0". Why not?

线程 A 在设置 flag = true 之前设置 i = 7.JLS 保证如果单个线程在执行第二个语句之前执行一个语句,那么第一个语句发生在"之前.第二个声明.(这听起来非常明显,但同样,它不应该.很多与线程有关的事情明显.)

Thread A set i = 7 before it set flag = true. The JLS guarantees that if a single thread executes one statement before it executes a second statement, then the first statment "happens before" the second statement. (That sounds stupendously obvious, but again, it shouldn't. A lot of things having to do with threads are not obvious.)

线程 B 在打印 i 之前测试 flag.所以 *IF* 线程 A 之前设置了 flag=true 那么我们知道 i 必须等于 7: 传递性:i=7 发生在之前"flag=true,以及对 volatile flag 的写入,如果它发生了,发生在之前"读取相同的 flag.

Thread B tests flag before it prints i. So *IF* thread A previously set flag=true then we know that i must equal 7: Transitivity: i=7 "happens before" flag=true, and the write to volatile flag, IF IT HAPPENED AT ALL, "happens before" a read of the same flag.

如果真的发生了

数据竞争和竞争条件

要记住的最重要的事情是,当 JLS 承诺 A之前发生"时B,他们并不是说 A 总是确实发生在 B 之前:他们是说您可以依赖这种传递关系.他们说如果 A 确实发生在 B 之前,那么所有之前发生"的事情都会发生在 B 之前.A 也必须实际发生在 B 之前.

The biggest thing to remember is that when the JLS promises that A "happens before" B, they are not saying that A always actually does happen before B: They are saying that you can depend on that transitive relationship. They are saying that if A actually did happen before B, then all of the things that "happens before" A must also have actually happened before B.

程序可以打印Bleah!";因为没有什么可以阻止线程 B 在线程 A 设置 flag 之前测试它.有些人称之为数据竞赛".这两个线程是赛跑"的.看看哪个先到达 flag,程序的结果取决于哪个线程赢得了这场比赛.

The program can print "Bleah!" because nothing prevents thread B from testing the flag before thread A sets it. Some people call that a "data race." The two threads are "racing" to see which one gets to the flag first, and the outcome of the program depends on which thread wins that race.

当程序的正确性取决于数据竞争的结果时,我们中的一些人称之为竞争条件",这是一个真正的头痛.无法保证具有竞争条件的程序不会在您的测试期间做一千次正确的事情,然后在对您的客户最重要的时候做错误的事情.

When the correctness of a program depends on the outcome of a data race, some of us call that a "race condition," and that's a real headache. There's no guarantee that a program with a race condition won't do the right thing a thousand times during your testing, and then do the wrong thing when it matters most for your customer.

这篇关于变量应该在 2 个正在运行的线程之间是 volatile 吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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