Java 变量是否应该在两个运行线程之间易变?
在这种情况下,Java 变量是否应该在两个运行线程之间易变?,java,multithreading,volatile,Java,Multithreading,Volatile,在这种情况下,inta是否应该是易失性的,以保证线程之间的可见性 private volatile static int a = 0; public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { a = 10; } })
inta
是否应该是易失性的,以保证线程之间的可见性
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
,从阅读开始;首先
然后,要完全理解正在发生的事情,你需要知道是什么,以及
要将其简化,请查看以下内容:
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
看到了对a
的易失性写入。这是因为从写入线程到读取线程都建立了一个“之前发生”,并且在a=10
之前执行的每个操作都保证对看到a
为10
的线程可见
你能再解释一下“以前发生过”吗
关于“以前发生过”最重要的一点是,它是一个。这意味着,如果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 boolean flag = false;
/*non-volatile*/ int i = 0;
假设线程A执行以下操作:
i = 7;
flag = true;
if (flag) {
System.out.println(i);
}
else {
System.out.println("Bleah!");
}
假设线程B执行以下操作:
i = 7;
flag = true;
if (flag) {
System.out.println(i);
}
else {
System.out.println("Bleah!");
}
线程B可以打印“7”,也可以打印“Bleah!”,但由于“before”保证,我们完全知道线程B永远不会打印“0”。为什么不呢
在设置前,先执行设置i=7
,然后再设置flag=true
。JLS保证,如果单个线程在执行第二条语句之前执行一条语句,那么第一条语句“发生在”第二条语句之前。(这听起来非常明显,但也不应该如此。许多与线程有关的事情并不明显。)
线程B在打印i
之前测试flag
。因此,如果*线程一个先前设置的flag=true
,那么我们知道i
必须等于7
:传递性:i=7
“发生在”flag=true
之前,而写入volatile标志
,如果它发生在读取相同的flag
之前
如果真的发生了
数据竞争和竞争条件
要记住的最大一点是,当JLS承诺A“发生在”B之前时,他们并不是说A实际上总是发生在B之前:他们是说你可以依赖于这种传递关系。他们说如果A确实发生在B之前,那么所有“发生在”A之前的事情肯定也发生在B之前
程序可以打印“Bleah!”,因为在线程A设置标志之前,没有任何东西可以阻止线程B测试该标志。有些人称之为“数据竞赛”。两个线程“竞赛”以查看哪个线程首先到达标志,程序的结果取决于哪个线程赢得了这场竞赛
当程序的正确性取决于数据竞争的结果时,我们中的一些人称之为“竞争条件”,这是一个真正令人头痛的问题。无法保证具有竞争条件的程序在测试期间不会做上千次正确的事情,然后在对客户最重要的时候做错误的事情。1)您知道哪个线程首先启动吗?(提示:您不需要)2)volatile
是关于两个实体的-当其他线程看到某些写入时,这意味着在建立之前发生……您能进一步解释一下“之前发生”吗?你没有真正回答在这种情况下是否需要volatile关键字。问题是,在这个级别上,很难了解基础知识并正确应用它。但是在这种情况下,制作一个volatile就可以了(正如Eugene已经指出的那样,它建立了发生之前的关系)。因为hb(b=100,a=10)因为它们是在同一个线程中按程序顺序执行的动作(规则1),而a==10
意味着hb(a=10,t2),这意味着规则4中的hb(b=100,t2)-对吗?@daniu是的,但当然,您需要保持在JLS
的适当范围内。我只是在这里把它的表面擦掉了。但真的很开心。