在下面的代码中,如果我在for循环中使用sysout语句然后代码执行并在条件满足后进入循环内部但如果我不在循环内使用sysout语句然后无限循环继续而不进入if内部条件,即使条件满足..任何人都可以请帮助我找出确切的原因。只是一个sysout语句使if条件成为真。为什么会这样?
代码如下: -
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name){
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
for(;;)
{
//Output 1: without this sysout statement.
//Output 2: After uncommenting this sysout statement
//System.out.println(Thread.currentThread().isInterrupted());
if(TestThread.i>3)
{
try {
for(int j = 4; j > 0; j--) {
System.out.println("Thread: " + threadName + ", " + j);
}
} catch (Exception e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
}
}
public void start ()
{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
static int i=0;
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i+=4;
System.out.println(i);
}
}
无限循环中没有sysout语句的输出: - enter image description here
在无限循环中使用sysout语句输出: - enter image description here
答案 0 :(得分:3)
此处的问题可以通过更改
来解决 static int i=0;
到
static volatile int i=0;
制作变量volatile
会产生许多复杂的后果,我不是这方面的专家。所以,我试着解释一下我对它的看法。
变量i
存在于主内存和RAM中。但是RAM很慢,所以你的处理器将它复制到更快(更小)的内存:缓存。事实上,多个缓存,但那是无关紧要的。
但是当两个不同处理器上的两个线程将它们的值放在不同的缓存中时,当值发生变化时会发生什么?好吧,如果线程1更改了缓存1中的值,则线程2仍然使用缓存2中的旧值。除非我们告诉两个线程这个变量i
可能随时都在变化,就好像它是魔术一样。这就是volatile关键字的作用。
那么为什么它与print语句一起使用?嗯,print语句在幕后调用了很多代码。这些代码中的一些很可能包含一个synchronized块或另一个volatile变量,它(在某种程度上)也刷新了两个缓存中i
的值。 (感谢Marco13指出这一点。)
下次尝试访问i
时,您会获得更新后的值!
这也是一个很好的解释(带图片!):
答案 1 :(得分:2)
当您访问变量值时,每次都不会将更改写入(或从中加载)实际内存位置。可以将该值加载到CPU寄存器中,也可以将其缓存,然后静置,直到刷新缓存。此外,因为TestThread.i
根本没有在循环内修改,优化器可能会决定在循环之前用检查替换它,并完全摆脱if
语句(我不认为它)实际上是在你的情况下发生,但关键是可能)。
使线程刷新其缓存并将其与物理内存的当前内容同步的指令称为 内存屏障 。 Java中有两种强制内存障碍的方法:进入或退出synchronized
块或访问volatile
变量。
当其中任何一个事件发生时,刷新缓存,并保证线程既可以看到内存内容的最新视图,又可以将它在本地提交给内存的所有更改。
因此,正如评论中所建议的,如果您将TestThread.i
声明为volatile
,问题就会消失,因为无论何时修改该值,都会立即提交更改,并且优化程序会知道不要优化,the离开循环,而不是缓存值。
现在,为什么添加print语句会改变行为?嗯,io内部有很多同步,线程在某处遇到内存屏障,并加载新值。这只是巧合。