我是Java的新手,以下内容可能很明显,但令我感到困惑。请考虑以下代码:
while(1>0){
if(x!=0){
//do something
}
}
x变量在另一个线程中更改。但是,即使x不为零,也不会执行if语句中的代码。如果我通过以下
更改代码while(1>0){
System.out.println("here");
if(x!=0){
//do something
}
}
当x不再为零时,现在执行if语句中的代码。我怀疑这与Java编译器的规则有关,但它对我来说非常混乱。任何澄清这一点的帮助将不胜感激。
答案 0 :(得分:7)
如果x
在另一个线程中发生变化,那么您可能会看到一个副作用,即您没有同步访问该变量。
Java内存和线程模型非常复杂,因此我建议您获取Brain Goetz的Java Concurrency in Practice副本并阅读。
简短的回答是确保x
块中包含synchronized
的访问权限:
while (1 > 0) {
int temp;
synchronized (this) {
temp = x;
}
if (temp != 0) {
// Do something
}
}
同样在修改x
。
请注意,此示例将x
存储在临时变量中,因为您希望同步块尽可能小 - 它们会强制执行互斥锁定,因此您不希望在此处执行太多操作。
或者,您可以将x
声明为volatile
,这可能足以满足您的使用案例。我建议您使用synchronized
版本,因为您最终需要知道如何正确使用synchronized
,所以您现在也可以学习它。
答案 1 :(得分:2)
如果您使用的是多线程代码,请检查x变量是否为volatile。
答案 2 :(得分:1)
没有System.out.println("here");
没有任何反应的原因,卡梅伦斯金纳的回答很好地解释了这一点。
那么为什么使用if(x!=0)
时println
内的块会起作用? println
执行synchronized
阻止(请参阅PrintStream.write(String s)
)。这会强制当前线程从主内存中检索System.out
状态并更新线程的缓存,然后让线程执行任何进一步的代码行。令人惊讶的副作用是,其他变量的状态(例如您的x
)也会以这种方式更新,尽管x
的锁定不涉及同步。它被称为搭载。
如果我将使用自由文本来描述Java Memory Model Specification中描述的手续:据说在释放锁定之前执行的操作发生在操作之后执行操作获得锁定。
我将用一个例子来演示它。假设Thread 1
已执行,并且只有在完成后才会启动Thread 2
。还假设x
,y
和z
是两个线程共享的变量。请注意,我们只能在z
的{{1}}块内确定synchronized
的值。
y
这当然是依赖于同步的非常糟糕的做法 ......
答案 3 :(得分:0)
这可能是编译器优化。它认识到,在while循环的范围内,变量永远不会改变,并且会缓存值而不是每次从内存中读取它。为了避免这种行为,简单地将变量声明为volatile:
private volatile int x;
答案 4 :(得分:0)
还检查你在if子句中做了什么。它们只是内存操作,它比写入控制台快得多,所以在更改状态之前,你在最后一种情况下执行更多的操作。