2013-06-19 50 views
4

我正在学习编写更好的多线程程序,线程安全和确定性。我碰到这段代码多线程:为什么输出?它是确定性的吗?

// File Name : Callme.java 
// This program uses a synchronized block. 
    class Callme { 
    void call(String msg) { 
     System.out.print("[" + msg); 
     try { 
      Thread.sleep(1000); 
     } catch (InterruptedException e) { 
      System.out.println("Interrupted"); 
     } 
     System.out.println("]"); 
    } 
    } 

    // File Name : Caller.java 
    class Caller implements Runnable { 
    String msg; 
    Callme target; 
    Thread t; 
    public Caller(Callme targ, String s) { 
     target = targ; 
     msg = s; 
     t = new Thread(this); 
     t.start(); 
    } 

    // synchronize calls to call() 
    public void run() { 
     synchronized(target) { // synchronized block 
      target.call(msg); 
     } 
    } 
    } 
    // File Name : Synch.java 
    public class Synch { 
    public static void main(String args[]) { 
     Callme target = new Callme(); 
     Caller ob1 = new Caller(target, "Hello"); 
     Caller ob2 = new Caller(target, "Synchronized"); 
     Caller ob3 = new Caller(target, "World"); 

     // wait for threads to end 
     try { 
      ob1.t.join(); 
      ob2.t.join(); 
      ob3.t.join(); 
     } catch(InterruptedException e) { 
      System.out.println("Interrupted"); 
     } 
    } 
    } 

产生以下输出(试过〜100倍)

[Hello] 
[World] 
[Synchronized] 

所以我的第一个问题是,这是保证输出?我还观察到,如果我改变睡眠100它仍然产生相同的输出,但如果我改变睡眠10输出变为

[Hello] 
[Synchronized] 
[World] 

第二个问题是,如果它是有保证的,为什么呢?最后但并非最不重要的,为什么这个输出?我预计它是

[Hello] 
[Synchronized] 
[World] 
+0

您需要先编辑outputBuffer。然后冲洗它。 –

+0

@ huseyintugrulbuyukisik-outputBuffer? – vidit

+0

你是否在同步所有线程,例如:当第一个线程完成时,它表明第二个线程要写入。第二完成和信号第三写。 –

回答

3

我认为这里有两件很有意思的事情。

代码尝试依赖同步块来保持呼叫的顺序一致。这有两个问题:

1)同步块不公平(请参阅Synchronization vs Lock),所以哪个永远线程先到达锁定的同步块可能不是第一个被授予对该对象的访问权限。然而,根据那篇文章,在方法级别public synchronized void run()的同步将是一个公平的锁(在Java 1.5中,而不是在Java 1.6中),所以等待锁的第一个锁将是第一个被授予访问对象的权限。

2)即使所述同步块是公平的,第一螺纹可以不,在理论上,(在[同步])可能不是第一调用一些代码在运行()。 [世界]实际上可以首先调用它。

+0

1)是错误的,在同步方法中没有公平性,并且使run方法同步将不会改变_even,如果它是fair_。 – jtahlborn

+0

有关此主题的完整讨论,请参见Brian Goetz的“Java并发实践”一书,第13.3节。这是我在引用的帖子中提出的声明,同步方法是公平的(当在方法级别指定时)。我并没有说它会改变它不确定的事实。这一块不公平的事实只会给另一层不确定的行为增加另一层不可预测性。 – jmpyle771

+0

因此,即使同步块是公平的,原始的不确定性层仍然存在。 – jmpyle771

2

不,输出不能保证。

+0

为什么我总是得到那个输出是否有任何理由? – vidit

+3

竞赛条件在测试场景中倾向于“确定性”,并且只有在负载峰值(即生产中)时才会中断。这就是让他们如此“有趣”追踪的原因。 – jtahlborn

1

没有输出订单保证;输出是完全随机的,因为它由操作系统决定哪个线程应该在一定的时间分配CPU。将线程时间分配给CPU的算法是非确定性的。

为了让代码打印你好,同步世界顺序,你应该改变Synch到:

Caller ob1 = new Caller(target, "Hello"); 
ob1.t.join(); 
Caller ob2 = new Caller(target, "Synchronized"); 
ob2.t.join(); 
Caller ob3 = new Caller(target, "World"); 
ob3.t.join(); 

也没有必要为​​块(它的方式是写),因为只有一个线程将调用run方法;也只有msg被读取,没有被写入导致任何问题,并且方法不会以任何方式改变target的状态。

1

输出的顺序不能保证。​​块可以防止交错的不同线程打印输出,但它不会确保三个不同输出的任何顺序。不幸的是,你看到的“确定性的”行为只是偶然。

+0

但是,由于CPU上线程的时间长短以及正在运行的代码的数量,可能性很高。:)但是,仍然有机会。 – selig