2016-01-07 45 views
5

在以下代码中,如果我在循环内部使用sysout语句,那么代码将在条件满足时执行并进入循环,但如果我不在内部使用sysout语句然后循环然后无限循环继续而不进入if条件,即使if条件满足..任何人都可以请帮我找出确切的原因。只是一个sysout语句使if条件成为true。为什么这样?由于system.out.println语句导致运行线程延迟

的代码如下: -

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); 
    } 
} 

没有系统输出语句在无限循环输出: - enter image description here

输出与系统输出语句在无限循环: - enter image description here

+2

我也会想你正在运行成挥发性的问题。尝试制作 static int i = 0; static volatile int i = 0;如果现在的行为与生病相同,则用解释提出答案。 –

+0

@Aaron它是一个无限循环,所以他们不断地执行它,正确...?编辑:亚伦离开大楼。 –

+1

@亨克德波尔,我尝试使用易失性,它工作正常。但是,你能解释一下sysout语句做了哪些改变,即使没有使用volatile关键字,代码也能工作。 –

回答

3

这里的问题可以通过改变

static int i=0;

被固定到

static volatile int i=0;

制作一个变量volatile有许多复杂的后果,我不是这方面的专家。所以,我会试着解释我如何看待它。

变量i住在你的主存,你的RAM。但是RAM速度很慢,所以你的处理器将它复制到更快(和更小)的内存中:缓存。事实上有多个缓存,但那是不相关的。

但是当两个不同处理器上的两个线程将它们的值放在不同的缓存中时,值发生了什么变化?那么,如果线程1更改了高速缓存1中的值,线程2仍然使用高速缓存2中的旧值。除非我们告诉两个线程此变量i可能随时都在变化,就好像它是魔术一样。这就是volatile关键字的作用。

那么,为什么它与print语句一起工作呢?那么,print语句在幕后调用了很多代码。这些代码中的一些很可能包含一个同步块或另一个volatile变量,这个变量也会刷新两个缓存中的值i。 (感谢Marco13指出了这一点)。

下次尝试访问i时,会得到更新的值! PS:我在这里说RAM,但它可能是两个线程之间最接近的共享内存,如果它们是超线程的,那么它可能是一个缓存。

这是一个很好的解释太(含图片!):

http://tutorials.jenkov.com/java-concurrency/volatile.html

+0

我还想问一件事,如果我使用thread.sleep而不是sysout语句,即使这样if条件也成立。 Thread.sleep是否也会占用一些内存并清除缓存中的数据? –

+0

@ Marco13好的,我会修改答案。感谢您的更正! –

+0

@MeenakshiKumari最好的方法是自己测试一下! :)但是,不,我不这么认为,除非(再次偶然),一些外来的代码会在睡眠运行时触发清除缓存。就像我说的,我不是专家,在实践中线程通常比看起来复杂得多。所以我很犹豫在这里给你非常具体的规则,对不起。睡觉本身就是告诉操作系统,你现在没有什么可做的事情了,没有其他的了,所以你不应该依赖它有任何特殊的属性,如刷新内存。 –

2

在访问变量值时,cha nges不会每次写入(或加载)实际的内存位置。该值可以加载到CPU寄存器或缓存,并在那里坐直到缓存刷新。此外,因为TestThread.i在循环内部没有被修改,所以优化器可能会决定在循环之前用一个检查来替换它,并且完全摆脱if语句(我不认为它实际上发生在您的情况中,但重点是它可能可能)。

,使线程刷新其高速缓存和它们的物理内存的当前内容同步的指令被称为存储器屏障。 Java中有两种强制内存屏障的方法:输入或退出​​块或访问volatile变量。 当其中任何一个事件发生时,缓存被刷新,并且线程保证能够看到内存内容的最新视图,并且具有它在本地提交到内存的所有更改。

所以,在意见提出,如果你申报TestThread.ivolatile,这个问题就会迎刃而解,因为每当值被修改,更改的内容将立即提交,并且优化器将不知道要优化器,电子支票远离循环,而不是缓存该值。

现在,为什么添加打印语句会改变行为?好吧,io内部有很多同步进行,线程在某个地方碰到内存障碍,并加载新的值。这只是一个巧合。

+0

我并不是说你错了,而是允许优化器对成员变量做出如此沉重的假设?似乎容易出错。 –

+0

当然,为什么不呢?如果循环内部没有内存屏障,并且当前线程未修改变量,则认为它保持不变是完全安全的。事实上,做这个假设比不做它更安全,因为它使行为具有确定性。 – Dima

+0

是的,但是它在编译时如何知道在线程产生之前我的值不会改变?也许我错过了什么。 –