我使用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)。
谢谢:)
答案 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++;
}