Java Thread:解释为什么不总是打印变量的最新值

时间:2012-12-22 18:00:43

标签: java multithreading

我使用Java Thread获得了这段代码:

public class ThreadExample implements Runnable{

    public Thread thread;
    static int i = 0;
    ThreadExample(){
        thread = new Thread(this);
        thread.start();
    }

    public static void main(String[] args) {
        ThreadExample example = new ThreadExample();
        for(int n =0; n<1000; n++){
            System.out.println("main thread "+i);
            i++;
        }
    }

    public void run(){
        for(int index=0; index<1000; index++){
            System.out.println("Sub thread "+i);
            i++;
        }
    }
}

运行时,结果是:

main thread 0
Sub thread 0
Sub thread 2
Sub thread 3
Sub thread 4
Sub thread 5
Sub thread 6
Sub thread 7
Sub thread 8
Sub thread 9
Sub thread 10
Sub thread 11
Sub thread 12
main thread 1
....

我知道运行的线程不遵循它的顺序。但是,我不明白的是:为什么主线程打印1(它打印变量i),当变量i到达12? (因为subthread已打印到12)。

谢谢:)

4 个答案:

答案 0 :(得分:6)

最有可能的解释是,在准备打印文本和实际打印文本之间存在很大的延迟。主线程准备了语句“主线程1”,然后不得不等到子线程释放System.out上的锁,并获得锁,以便实际打印它。

如果你使i易变,并且它仍然发生,那么这几乎是唯一的解释。

答案 1 :(得分:1)

这是因为如果没有某些同步或volatile关键字,线程将不会看到彼此的修改。

请参阅http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4

答案 2 :(得分:1)

结果可能是由于锁获取(发生在System.out.println调用的封面下)的方式本身并不公平。不公平的实现,或者至少部分不公平的实现,通常不能保证线程将按照它们等待的顺序获取锁。

结果是,当两个线程在紧密循环中反复竞争锁定时(如在您的示例中),您通常会通过一个线程进行一次采集,然后通过另一个线程进行一系列采集,因此上。不公平的锁通常更容易实现,并且通常可以提高性能(吞吐量)而不是高度竞争锁的完美公平替代方案,因为它们不会受到lock convoy影响。显而易见的缺点是,对特定等待线程获取锁定的顺序没有任何保证,理论上一些等待线程可以明确地缺乏(实际上这可能不会发生,或者锁可能设计为当某些线程等待不寻常的时间时,回退到公平行为。)

鉴于不公平的锁定,这种模式很自然。第二个线程中的循环获得了锁定,在第一个线程读取1并开始等待之后不久 - 此时输出的字符串已经与前导文本连接,因此值1为“烘焙”。主线程必须等待另一个线程上的几个锁定/解锁对,它有机会运行,并打印旧值。

另一种解释是,由于缺少volatile,解释器或JIT甚至不再读取i的共享值,而是将变量托管到寄存器中 - 这是JMM和JVM规范允许的。没有可能会改变我的干预方法。

答案 3 :(得分:0)

据我所知,这是因为System.out

的缓冲

尝试在每个printlen之后写入文件或致电System.out.flush()

并尝试通过同步方法增加i

public synchronized void increment() {
  i++;
}