3個線程依次輪流打印出75個數

轉自:詳解使用synchronized解決三個線程依次輪流打印出75個數



原帖見:http://www.iteye.com/topic/1117703

問題描述:

一個關於線程的經典面試題,要求用三個線程,按順序打印1,2,3,4,5.... 71,72,73,74, 75.

線程1先打印1,2,3,4,5, 然後是線程2打印6,7,8,9,10, 然後是線程3打印11,12,13,14,15. 接着再由線程1打印16,17,18,19,20....以此類推, 直到線程3打印到75。

 

直接上代碼:

 

Java代碼  收藏代碼
  1. package concurrent.test;  
  2.   
  3.   
  4. /** 
  5.  * 要求創建三個線程,輸出1-75, 
  6.  * 最開始第一個線程輸出1-5,第二個輸出6-10,第三個輸出11-15 
  7.  * 接着再第一個線程輸出16-20...就這樣循環下去,直到打印出75個數 
  8.  * @author qiaoxueshi 
  9.  * 
  10.  */  
  11. public class Print1to75 {  
  12.     static class Printer implements Runnable{  
  13.         static int num = 1//開始數字  
  14.         static final int END = 75;  
  15.         int id;  
  16.           
  17.         public Printer(int id) {  
  18.             this.id = id;  
  19.         }  
  20.           
  21.         @Override  
  22.         public void run(){  
  23.             synchronized (Printer.class) {  
  24.                 while(num <= END){  
  25.                     if(num / 5 % 3 == id){ //如果是屬於自己的數,依次打印出來五個  
  26.                         System.out.print(id + ":");  
  27.                         for(int i = 0; i < 5; i++){  
  28.                             System.out.print(num++ + ", ");  
  29.                         }  
  30.                         System.out.println();  
  31.                           
  32.                         Printer.class.notifyAll();//放棄CPU使用權,喚醒等待在Print.class隊列上的的打印線程  
  33.                     }else{  
  34.                         try {  
  35.                             Printer.class.wait();//如果不屬於自己的數,把當前線程掛在Printer.class這個對象的等待隊列上(也是放棄CPU使用權),等待喚醒  
  36.                         } catch (InterruptedException e) {  
  37.                             System.out.println("id" + "被打斷了");  
  38.                         }  
  39.                     }  
  40.                 }  
  41.             }  
  42.         }  
  43.     }  
  44.       
  45.       
  46.     public static void main(String[] args) {  
  47.         //下面可以不按0,1,2的順序來,而且在兩兩中間隨便sleep(),都會正確打印出來  
  48.         new Thread( new Printer(0)).start();  
  49.         new Thread( new Printer(1)).start();  
  50.         new Thread( new Printer(2)).start();  
  51.     }  
  52. }  

 

註釋中說的也很明白,有問題歡迎大家討論。

 

 

 

結果(運行了N次,結果都是一致的,請大家檢驗):

 

Java代碼  收藏代碼
  1. 0:12345,   
  2. 1:678910,   
  3. 2:1112131415,   
  4. 0:1617181920,   
  5. 1:2122232425,   
  6. 2:2627282930,   
  7. 0:3132333435,   
  8. 1:3637383940,   
  9. 2:4142434445,   
  10. 0:4647484950,   
  11. 1:5152535455,   
  12. 2:5657585960,   
  13. 0:6162636465,   
  14. 1:6667686970,   
  15. 2:7172737475,   

 

 

注意第23行的synchronized (Printer.class) ,爲什麼是Printer.class,而不是this呢?

是因爲Print.class也是一個對象,在當前JVM中是唯一的,它相當於一個“公證人”,三個線程競爭資源的時候都是從唯一的這個“公證人”手裏拿到許可,才能進入synchronized體。

而如果是synchronized (this)的話,this也相當於一個“公證人”,那麼三個線程各自有一個“公證人”,相當於各幹各的,三個中間沒有競爭關係,構不成同步。

 

可見只要是這三個的“公證人”是同一個傢伙,就能保持同步,稍微修改一下代碼,我們給三個線程傳進去同一個“公證人”(其實就是一個普通的不能再普通的對象):

 

 

Java代碼  收藏代碼
  1. package concurrent.test;  
  2.   
  3.   
  4. /** 
  5.  * 要求創建三個線程,輸出1-75, 
  6.  * 最開始第一個線程輸出1-5,第二個輸出6-10,第三個輸出11-15 
  7.  * 接着再第一個線程輸出16-20...就這樣循環下去,直到打印出75個數 
  8.  * @author qiaoxueshi 
  9.  * 
  10.  */  
  11. public class Print1to75 {  
  12.     static class Printer implements Runnable{  
  13.         static int num = 1//開始數字  
  14.         static final int END = 75;  
  15.         int id;  
  16.         Object o; //這就是三個線程的“公證人”,有點寒酸吧  
  17.           
  18.         public Printer(int id, Object o) {  
  19.             this.id = id;  
  20.             this.o = o;  
  21.         }  
  22.           
  23.         @Override  
  24.         public void run(){  
  25.             synchronized (o) {  
  26.                 while(num <= END){  
  27.                     if(num / 5 % 3 == id){ //如果是屬於自己的數,依次打印出來五個  
  28.                         System.out.print(id + ":");  
  29.                         for(int i = 0; i < 5; i++){  
  30.                             System.out.print(num++ + ", ");  
  31.                         }  
  32.                         System.out.println();  
  33.                           
  34.                         o.notifyAll();//放棄CPU使用權,喚醒在o對象的等待隊列上的線程  
  35.                     }else{  
  36.                         try {  
  37.                             o.wait(); //如果不屬於自己的數,把當前線程掛在o這個對象的等待隊列上(也放棄了CPU使用權),等待喚醒  
  38.                         } catch (InterruptedException e) {  
  39.                             System.out.println("id" + "被打斷了");  
  40.                         }  
  41.                     }  
  42.                 }  
  43.             }  
  44.         }  
  45.     }  
  46.       
  47.       
  48.     public static void main(String[] args) {  
  49.         //下面可以不按0,1,2的順序來,而且在兩兩中間隨便sleep(),都會正確打印出來  
  50.         Object o = new Object();  
  51.         new Thread( new Printer(0, o)).start();  
  52.         new Thread( new Printer(1, o)).start();  
  53.         new Thread( new Printer(2, o)).start();  
  54.     }  
  55. }  
 

 

在第16行,添加了三個線程的”公證人“ Object o;

第25,34,37行都由原來的Printer.class改爲了o;

在第50行,創建了一個Object對象,傳給了三個線程。

運行結果和上面的是一模一樣地!

 

 

Java代碼  收藏代碼
  1. new Thread( new Printer(0, o)).start();  
  2. new Thread( new Printer(1, o)).start();  
  3. new Thread( new Printer(2, o)).start();  

 如果覺得這段不太優雅,可以使用ExecuorService來實現,道理是一樣的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章