三個線程順序打印ABC


題:建立三個線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時運行,交替打印10次ABC。這個問題用Object的wait(),notify()就可以很方便的解決。代碼如下:

 1 public class MyThreadPrinter2 implements Runnable {     
 2         
 3     private String name;     
 4     private Object prev;     
 5     private Object self;     
 6     
 7     private MyThreadPrinter2(String name, Object prev, Object self) {     
 8         this.name = name;     
 9         this.prev = prev;     
10         this.self = self;     
11     }     
12     
13     @Override    
14     public void run() {     
15         int count = 10;     
16         while (count > 0) {     
17             synchronized (prev) {     
18                 synchronized (self) {     
19                     System.out.print(name);     
20                     count--;    
21                       
22                     self.notify();     
23                 }     
24                 try {     
25                     prev.wait();     
26                 } catch (InterruptedException e) {     
27                     e.printStackTrace();     
28                 }     
29             }     
30     
31         }     
32     }     
33     
34     public static void main(String[] args) throws Exception {     
35         Object a = new Object();     
36         Object b = new Object();     
37         Object c = new Object();     
38         MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);     
39         MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);     
40         MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);     
41              
42              
43         new Thread(pa).start();  
44         Thread.sleep(100);  //確保按順序A、B、C執行  
45         new Thread(pb).start();  
46         Thread.sleep(100);    
47         new Thread(pc).start();     
48         Thread.sleep(100);    
49         }     
50 }    

輸出結果:ABCABCABCABCABCABCABCABCABCABC

  先來解釋一下其整體思路,從大的方向上來講,該問題為三線程間的同步喚醒操作,主要的目的就是ThreadA->ThreadB->ThreadC->ThreadA循環執行三個線程。為了控制線程執行的順序,那么就必須要確定喚醒、等待的順序,所以每一個線程必須同時持有兩個對象鎖,才能繼續執行。一個對象鎖是prev,就是前一個線程所持有的對象鎖。還有一個就是自身對象鎖。主要的思想就是,為了控制執行的順序,必須要先持有prev鎖,也就前一個線程要釋放自身對象鎖,再去申請自身對象鎖,兩者兼備時打印,之后首先調用self.notify()釋放自身對象鎖,喚醒下一個等待線程,再調用prev.wait()釋放prev對象鎖,終止當前線程,等待循環結束后再次被喚醒。運行上述代碼,可以發現三個線程循環打印ABC,共10次。程序運行的主要過程就是A線程最先運行,持有C,A對象鎖,后釋放A,C鎖,喚醒B。線程B等待A鎖,再申請B鎖,后打印B,再釋放B,A鎖,喚醒C,線程C等待B鎖,再申請C鎖,后打印C,再釋放C,B鎖,喚醒A。看起來似乎沒什么問題,但如果你仔細想一下,就會發現有問題,就是初始條件,三個線程按照A,B,C的順序來啟動,按照前面的思考,A喚醒B,B喚醒C,C再喚醒A。但是這種假設依賴於JVM中線程調度、執行的順序。

wait和sleep區別
共同點: 

1. 他們都是在多線程的環境下,都可以在程序的調用處阻塞指定的毫秒數,並返回。 
2. wait()和sleep()都可以通過interrupt()方法 打斷線程的暫停狀態 ,從而使線程立刻拋出InterruptedException。 
   如果線程A希望立即結束線程B,則可以對線程B對應的Thread實例調用interrupt方法。如果此刻線程B正在wait/sleep /join,則線程B會立刻拋出InterruptedException,在catch() {} 中直接return即可安全地結束線程。 
   需要注意的是,InterruptedException是線程自己從內部拋出的,並不是interrupt()方法拋出的。對某一線程調用 interrupt()時,如果該線程正在執行普通的代碼,那么該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到 wait()/sleep()/join()后,就會立刻拋出InterruptedException 。 
不同點: 
1. Thread類的方法:sleep(),yield()等 
   Object的方法:wait()和notify()等 
2. 每個對象都有一個鎖來控制同步訪問。Synchronized關鍵字可以和對象的鎖交互,來實現線程的同步。 
   sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。 
3. wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用 
4. sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常
所以sleep()和wait()方法的最大區別是:
    sleep()睡眠時,保持對象鎖,仍然占有該鎖;
    而wait()睡眠時,釋放對象鎖。
  但是wait()和sleep()都可以通過interrupt()方法打斷線程的暫停狀態,從而使線程立刻拋出InterruptedException(但不建議使用該方法)。
sleep()方法
sleep()使當前線程進入停滯狀態(阻塞當前線程),讓出CUP的使用、目的是不讓當前線程獨自霸占該進程所獲的CPU資源,以留一定時間給其他線程執行的機會;
   sleep()是Thread類的Static(靜態)的方法;因此他不能改變對象的機鎖,所以當在一個Synchronized塊中調用Sleep()方法是,線程雖然休眠了,但是對象的機鎖並木有被釋放,其他線程無法訪問這個對象(即使睡着也持有對象鎖)。
  在sleep()休眠時間期滿后,該線程不一定會立即執行,這是因為其它線程可能正在運行而且沒有被調度為放棄執行,除非此線程具有更高的優先級。 
wait()方法
wait()方法是Object類里的方法;當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到后還需要返還對象鎖);其他線程可以訪問;
  wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的線程。
  wiat()必須放在synchronized block中,否則會在program runtime時扔出”java.lang.IllegalMonitorStateException“異常。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM