2015-07-19 50 views
2
public class B { 
    public static String lock = "a"; 
    public static void main(String[] args) { 
     MyThread t1 = new MyThread("Thread 1"); 
     t1.start(); 

     lock = "b"; 
     MyThread t2 = new MyThread("Thread 2"); 
     t2.start(); 

     lock = "c"; 
     MyThread t3 = new MyThread("Thread 3"); 
     t3.start(); 

     lock = "d"; 
     MyThread t4 = new MyThread("Thread 4"); 
     t4.start(); 
    } 

} 

class MyThread extends Thread{ 

    public MyThread(String name) { 
     super(name); 
    } 

    @Override 
    public void run() { 
     synchronized (B.lock){ 
      System.out.println(Thread.currentThread().getName() +" is going to sleep for 5 seconds"); 
      try { 
       Thread.sleep(5000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println(Thread.currentThread().getName() + " done sleeping "); 
     } 
    } 
} 

輸出::爲什麼不是所有線程都同時啓動?

Thread 1 is going to sleep for 5 seconds 
Thread 2 is going to sleep for 5 seconds 
Thread 2 done sleeping 
Thread 1 done sleeping 
Thread 4 is going to sleep for 5 seconds 
Thread 4 done sleeping 
Thread 3 is going to sleep for 5 seconds 
Thread 3 done sleeping 

對不起,我不清楚的問題。但我的查詢是因爲我在每次線程啓動後都更改鎖對象,爲什麼不是所有線程都同時啓動並鎖定不同的字符串對象?我猜想可能是操作系統線程調度的原因。但每次執行都會導致僅同時啓動2個線程(1 & 2),而其餘2個線程(3 & 4)等待獲取鎖定。但爲什麼 ?

+1

它們不會同時啓動,因爲您沒有提供任何機制讓它們同時啓動。 –

+3

備註:請不要將字符串文字用作* locks *。如果你有多個類的代碼,你的*性能工程師會有一個名爲「程序太慢」的sev-1缺陷。接下來,你的預期輸出是什麼? – TheLostMind

+2

另外,在每個線程啓動之後對'lock'變量的賦值基本上會使'synchronized(B.lock)'結構失效。當線程1進入同步塊時,它擁有字符串「a」上的監視器。但是,您隨後用「b」覆蓋鎖定,因此當線程2進入同步塊時,它會獲取「b」上的監視器,並且能夠立即進入該塊。你爲什麼要改變'lock'變量的值? – schtever

回答

2

如果你想同時啓動所有的線程,你需要提供一些機制,使一個線程可以知道其他線程準備執行它開始工作之前。

有點像java.util.concurrent.CountDownLatch將有助於這一點。基本的想法是,你在線程中首先要做的是等待CountDownLatch;當所有線程都被創建並啓動時,您只能將鎖定計數到零。

例如,在你的main方法:

CountDownLatch latch = new CountDownLatch(4); 

MyThread t1 = new MyThread("Thread 1", latch); 
t1.start(); 
//... 
MyThread t4 = new MyThread("Thread 4", latch); 
t4.start();  

在你MyThread

class MyThread extends Thread{ 
    private final CountDownLatch latch; 
    public MyThread(String name, CountDownLatch latch) { 
     super(name); 
     this.latch = latch; 
    } 

    @Override 
    public void run() { 
     latch.countDown(); 
     latch.await(); 
     synchronized (B.lock){ 
      //... 
     } 
    } 
} 

線程將現在都試圖在同一時間進入​​塊。顯然,只有其中一個會在任何時候執行該塊。

+0

如果OP要求不是單獨啓動,而是同時運行所有線程,那麼我建議在'synchronized'塊中添加一個'MyThread'屬性作爲鎖。 – Turing85

+0

他可以簡單地在'latch'上同步,不是嗎?除了標題以外,我看不到他問任何問題。 –

+0

@AndyTurner我相信OP所要求的是爲什麼所有的線程都不能同時啓動。如果你運行這個程序,你會注意到並不是所有的線程都立即開始。 (在我的機器上,任何時候只能同時啓動兩個線程,其他兩個線程始終等待前兩個線程才能啓動) – CKing

1

一個教育暗示將嘗試在你的線程您的郵件打印的實際B.lock在一起:

@Override 
public void run() { 
    String currLock = B.lock; 
    synchronized (currLock){ 
     System.out.println(Thread.currentThread().getName() +" is going to sleep for 5 seconds locking " + currLock); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println(Thread.currentThread().getName() + " done sleeping "); 
    } 
} 

可能系統之間的不同,但我得到這樣的輸出:

 
Thread 1 is going to sleep for 5 seconds locking c 
Thread 3 is going to sleep for 5 seconds locking d 
Thread 1 done sleeping 
Thread 3 done sleeping 
Thread 2 is going to sleep for 5 seconds locking c 
Thread 4 is going to sleep for 5 seconds locking d 
Thread 4 done sleeping 
Thread 2 done sleeping 

(好吧,有時我會得到一些不同的結果,但讓我們看看我們在這裏得到了什麼)。

的時候線程1得鎖上的鎖,它已經改變了值的兩倍。所以它鎖定在"c"。線程2看起來也不幸得到了"c",並且因爲它已經被鎖定,所以它正在等待而不是打印。

通過時間線3運行,該數值已經變爲"d",這就是它的鎖定。線程4也獲得了該鎖,所以它也被延遲。

因此,一些延遲可能是由於操作系統無法真正運行的線程在一起。但是,還有一部分是由於簡單的事實,start()不能保證是採取main下一步驟之前開始run方法。到控制轉移到run()時,lock可能已經更改了兩次值。操作本來可以重新排序。

Thread.start()真的不說,該線程將立即啓動 - 它需要被安排和框架需要爲run方法有所準備,到那個時候,事情在其他線程可能會發生的事情 - 像字符串被分配,創建的其他線程等。

你幾乎可以保證線程1不會鎖定在"a"上,因此會出現一些鎖定比賽。

+0

請注意,我嘗試了相同的測試,但在我的情況下,所有線程都鎖定在「d」上,但尚未同時啓動。這就是爲什麼我沒有把這個解釋作爲答案。所有線程都鎖定在「d」上,但只有兩個線程可以同時運行?有一件事是肯定的,這個程序允許兩個線程在大多數機器上同時運行。爲什麼鎖的價值相同時,「d」超出了我的理解範圍。 – CKing

+0

@CKing,如果他們鎖定在同一個鎖上,他們如何同時啓動(如在,打印第一條消息),並在他們獲得鎖後打印?請注意,如果在鎖之前添加了另一個'println()',那麼您一直在同步'System.out'上的線程。 – RealSkeptic

+0

@CKing在進入'synchronized'塊之前是否在局部變量中存儲'B.lock'? – Turing85

2

此答案受Andy Turner's comment的啓發。

此事爲什麼程序的行爲,因爲它的作用:B.lock是不是你認爲它是值。我稍微修改了你的源代碼。

public class B { 
    public static String lock = "a"; 

    public static void main(String... args) { 
     lock = "a"; 

     MyThread t1 = new MyThread("Thread 1"); 
     t1.start(); 
     lock = "b"; 

     MyThread t2 = new MyThread("Thread 2"); 
     t2.start(); 
     lock = "c"; 

     MyThread t3 = new MyThread("Thread 3"); 
     t3.start(); 
     lock = "d"; 

     MyThread t4 = new MyThread("Thread 4"); 
     t4.start(); 
     // lock = "e"; 
    } 
} 

class MyThread extends Thread { 

    public MyThread(String name) { 
     super(name); 
    } 

    @Override 
    public void run() { 
     String s = B.lock; 
     synchronized (s) { 
      System.out.println( Thread.currentThread() 
             .getName() 
           + " is going to sleep for 5 seconds"); 
      try { 
       Thread.sleep(5000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println( Thread.currentThread().getName() 
           + " done sleeping "); 
     } 
     System.out.println(Thread.currentThread().getName() + ": " + s); 
    } 
} 

請理解,從這裏開始了,我只能說明我的機器的行爲,因爲行爲在很大程度上取決於你運行在主機系統上。但是,我提供的解決方案應該獨立於主機系統。

如果我執行這個代碼,我得到這樣的一些輸出:

線程2進入睡眠5秒

線3臨睡前5秒

主題1要睡5秒

線程1完成睡眠

線程1:b

線程3完成睡眠

線程2完成睡眠

線程2:C

線程3:d

螺紋4臨睡前5秒

螺紋4完成睡眠

線程4:d

所以,正如你所看到的,線程的開始稍微延遲了。此時,main方法已經更改了lock字段。因此最後一個線程起步較晚。

如果我對最後一行取消註釋,程序按預期運行(但這絕不是保證)。

要解決這個問題,我添加了一個Object lockMyThread並通過構造函數設置。

public class B { 
    public static String lock = "a"; 

    public static void main(String... args) { 
     lock = "a"; 

     MyThread t1 = new MyThread("Thread 1", lock); 
     t1.start(); 
     lock = "b"; 

     MyThread t2 = new MyThread("Thread 2", lock); 
     t2.start(); 
     lock = "c"; 

     MyThread t3 = new MyThread("Thread 3", lock); 
     t3.start(); 
     lock = "d"; 

     MyThread t4 = new MyThread("Thread 4", lock); 
     t4.start(); 
    } 
} 

class MyThread extends Thread { 
    Object lock; 

    public MyThread(String name, Object lock) { 
     super(name); 
     this.lock = lock; 
    } 

    @Override 
    public void run() { 
     synchronized (lock) { 
      System.out.println( Thread.currentThread() 
             .getName() 
           + " is going to sleep for 5 seconds"); 
      try { 
       Thread.sleep(5000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println( Thread.currentThread().getName() 
           + " done sleeping "); 
     } 
     System.out.println(Thread.currentThread().getName() + ": " + lock); 
    } 
} 

這樣,你去耦lock從它的實際設置的獲取和你有完全的控制上lock對象中Thread使用。

相關問題