小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

java多線程同步以及線程間通信詳解&消費(fèi)者生產(chǎn)者模式&死鎖&Thread.join()(多線程編程之二)

 沙門(mén)空海 2018-01-16
多線程系列教程:
java多線程-概念&創(chuàng)建啟動(dòng)&中斷&守護(hù)線程&優(yōu)先級(jí)&線程狀態(tài)(多線程編程之一)
java多線程同步以及線程間通信詳解&消費(fèi)者生產(chǎn)者模式&死鎖&Thread.join()(多線程編程之二)
java&android線程池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor淺析(多線程編程之三)
Java多線程:Callable、Future和FutureTask淺析(多線程編程之四)

本篇我們將討論以下知識(shí)點(diǎn):

1.線程同步問(wèn)題的產(chǎn)生

什么是線程同步問(wèn)題,我們先來(lái)看一段賣(mài)票系統(tǒng)的代碼,然后再分析這個(gè)問(wèn)題:
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月12日 下午2:55:42 
  5.  * @decrition 模擬賣(mài)票線程 
  6.  */  
  7. public class Ticket implements Runnable  
  8. {  
  9.     //當(dāng)前擁有的票數(shù)  
  10.     private  int num = 100;  
  11.     public void run()  
  12.     {  
  13.         while(true)  
  14.         {  
  15.                 if(num>0)  
  16.                 {  
  17.                     try{Thread.sleep(10);}catch (InterruptedException e){}  
  18.                     //輸出賣(mài)票信息  
  19.                     System.out.println(Thread.currentThread().getName()+".....sale...."+num--);  
  20.                 }  
  21.         }  
  22.     }  
  23. }  
上面是賣(mài)票線程類(lèi),下來(lái)再來(lái)看看執(zhí)行類(lèi):
  1. package com.zejian.test;  
  2. /**  
  3.  * @author zejian  
  4.  * @time 2016年3月12日 下午2:54:18  
  5.  * @decrition 模擬賣(mài)票系統(tǒng),該案例只考慮單方面賣(mài)票,其他情況暫時(shí)不考慮  
  6.  */  
  7. public class TicketDemo {  
  8.       
  9.     public static void main(String[] args)   
  10.     {  
  11.         Ticket t = new Ticket();//創(chuàng)建一個(gè)線程任務(wù)對(duì)象。  
  12.           
  13.         //創(chuàng)建4個(gè)線程同時(shí)賣(mài)票  
  14.         Thread t1 = new Thread(t);  
  15.         Thread t2 = new Thread(t);  
  16.         Thread t3 = new Thread(t);  
  17.         Thread t4 = new Thread(t);  
  18.         //啟動(dòng)線程  
  19.         t1.start();  
  20.         t2.start();  
  21.         t3.start();  
  22.         t4.start();  
  23.     }  
  24. }  
運(yùn)行程序結(jié)果如下(僅截取部分?jǐn)?shù)據(jù)):


從運(yùn)行結(jié)果,我們就可以看出我們4個(gè)售票窗口同時(shí)賣(mài)出了1號(hào)票,這顯然是不合邏輯的,其實(shí)這個(gè)問(wèn)題就是我們前面所說(shuō)的線程同步問(wèn)題。不同的線程都對(duì)同一個(gè)數(shù)據(jù)進(jìn)了操作這就容易導(dǎo)致數(shù)據(jù)錯(cuò)亂的問(wèn)題,也就是線程不同步。那么這個(gè)問(wèn)題該怎么解決呢?在給出解決思路之前我們先來(lái)分析一下這個(gè)問(wèn)題是怎么產(chǎn)生的?我們聲明一個(gè)線程類(lèi)Ticket,在這個(gè)類(lèi)中我們又聲明了一個(gè)成員變量num也就是票的數(shù)量,然后我們通過(guò)run方法不斷的去獲取票數(shù)并輸出,最后我們?cè)谕獠款?lèi)TicketDemo中創(chuàng)建了四個(gè)線程同時(shí)操作這個(gè)數(shù)據(jù),運(yùn)行后就出現(xiàn)我們剛才所說(shuō)的線程同步問(wèn)題,從這里我們可以看出產(chǎn)生線程同步(線程安全)問(wèn)題的條件有兩個(gè):1.多個(gè)線程在操作共享的數(shù)據(jù)(num),2.操作共享數(shù)據(jù)的線程代碼有多條(4條線程);既然原因知道了,那該怎么解決?
解決思路:將多條操作共享數(shù)據(jù)的線程代碼封裝起來(lái),當(dāng)有線程在執(zhí)行這些代碼的時(shí)候,其他線程時(shí)不可以參與運(yùn)算的。必須要當(dāng)前線程把這些代碼都執(zhí)行完畢后,其他線程才可以參與運(yùn)算。 好了,思路知道了,我們就用java代碼的方式來(lái)解決這個(gè)問(wèn)題。
2.解決線程同步的兩種典型方案
在java中有兩種機(jī)制可以防止線程安全的發(fā)生,Java語(yǔ)言提供了一個(gè)synchronized關(guān)鍵字來(lái)解決這問(wèn)題,同時(shí)在Java SE5.0引入了Lock鎖對(duì)象的相關(guān)類(lèi),接下來(lái)我們分別介紹這兩種方法
2.1通過(guò)鎖(Lock)對(duì)象的方式解決線程安全問(wèn)題
在給出解決代碼前我們先來(lái)介紹一個(gè)知識(shí)點(diǎn):Lock,鎖對(duì)象。在java中鎖是用來(lái)控制多個(gè)線程訪問(wèn)共享資源的方式,一般來(lái)說(shuō),一個(gè)鎖能夠防止多個(gè)線程同時(shí)訪問(wèn)共享資源(但有的鎖可以允許多個(gè)線程并發(fā)訪問(wèn)共享資源,比如讀寫(xiě)鎖,后面我們會(huì)分析)。在Lock接口出現(xiàn)之前,java程序是靠synchronized關(guān)鍵字(后面分析)實(shí)現(xiàn)鎖功能的,而JAVA SE5.0之后并發(fā)包中新增了Lock接口用來(lái)實(shí)現(xiàn)鎖的功能,它提供了與synchronized關(guān)鍵字類(lèi)似的同步功能,只是在使用時(shí)需要顯式地獲取和釋放鎖,缺點(diǎn)就是缺少像synchronized那樣隱式獲取釋放鎖的便捷性,但是卻擁有了鎖獲取與釋放的可操作性,可中斷的獲取鎖以及超時(shí)獲取鎖等多種synchronized關(guān)鍵字所不具備的同步特性。接下來(lái)我們就來(lái)介紹Lock接口的主要API方便我們學(xué)習(xí)
方法相關(guān)描述內(nèi)容                                                                                                                                                                                                                                                                                                                  
void lock()獲取鎖,調(diào)用該方法當(dāng)前線程會(huì)獲取鎖,當(dāng)獲取鎖后。從該方法返回
void lockInterruptibly()
throws InterruptedException
可中斷獲取鎖和lock()方法不同的是該方法會(huì)響應(yīng)中斷,即在獲取鎖
中可以中斷當(dāng)前線程。例如某個(gè)線程在等待一個(gè)鎖的控制權(quán)的這段時(shí)
間需要中斷。
boolean tryLock()嘗試非阻塞獲取鎖,調(diào)用該方法后立即返回,如果能夠獲取鎖則返回
true,否則返回false。
boolean tryLock(long time,TimeUnit unit)
throws  InterruptedException
超時(shí)獲取鎖,當(dāng)前線程在以下3種情況返回:
1.當(dāng)前線程在超時(shí)時(shí)間內(nèi)獲取了鎖
2.當(dāng)前線程在超時(shí)時(shí)間被中斷
3.當(dāng)前線程超時(shí)時(shí)間結(jié)束,返回false
void unlock()釋放鎖
Condition newCondition()條件對(duì)象,獲取等待通知組件。該組件和當(dāng)前的鎖綁定,當(dāng)前線程只有
獲取了鎖,才能調(diào)用該組件的await()方法,而調(diào)用后,當(dāng)前線程將縮放
鎖。
這里先介紹一下API,后面我們將結(jié)合Lock接口的實(shí)現(xiàn)子類(lèi)ReentrantLock使用某些方法。
ReentrantLock(重入鎖):
重入鎖,顧名思義就是支持重新進(jìn)入的鎖,它表示該鎖能夠支持一個(gè)線程對(duì)資源的重復(fù)加鎖,也就是說(shuō)在調(diào)用lock()方法時(shí),已經(jīng)獲取到鎖的線程,能夠再次調(diào)用lock()方法獲取鎖而不被阻塞,同時(shí)還支持獲取鎖的公平性和非公平性。這里的公平是在絕對(duì)時(shí)間上,先對(duì)鎖進(jìn)行獲取的請(qǐng)求一定先被滿足,那么這個(gè)鎖是公平鎖,反之,是不公平的。那么該如何使用呢?看范例代碼:
1.同步執(zhí)行的代碼跟synchronized類(lèi)似功能:
  1. ReentrantLock lock = new ReentrantLock(); //參數(shù)默認(rèn)false,不公平鎖    
  2. ReentrantLock lock = new ReentrantLock(true); //公平鎖    
  3.     
  4. lock.lock(); //如果被其它資源鎖定,會(huì)在此等待鎖釋放,達(dá)到暫停的效果    
  5. try {    
  6.     //操作    
  7. } finally {    
  8.     lock.unlock();  //釋放鎖  
  9. }    
2.防止重復(fù)執(zhí)行代碼:
  1. ReentrantLock lock = new ReentrantLock();    
  2. if (lock.tryLock()) {  //如果已經(jīng)被lock,則立即返回false不會(huì)等待,達(dá)到忽略操作的效果     
  3.     try {    
  4.         //操作    
  5.     } finally {    
  6.         lock.unlock();    
  7.    }    
  8. }    
3.嘗試等待執(zhí)行的代碼:
  1. ReentrantLock lock = new ReentrantLock(true); //公平鎖    
  2. try {    
  3.     if (lock.tryLock(5, TimeUnit.SECONDS)) {        
  4.         //如果已經(jīng)被lock,嘗試等待5s,看是否可以獲得鎖,如果5s后仍然無(wú)法獲得鎖則返回false繼續(xù)執(zhí)行    
  5.        try {    
  6.             //操作    
  7.         } finally {    
  8.             lock.unlock();    
  9.         }    
  10.     }    
  11. } catch (InterruptedException e) {    
  12.     e.printStackTrace(); //當(dāng)前線程被中斷時(shí)(interrupt),會(huì)拋InterruptedException                     
  13. }    
這里有點(diǎn)需要特別注意的,把解鎖操作放在finally代碼塊內(nèi)這個(gè)十分重要。如果在臨界區(qū)的代碼拋出異常,鎖必須被釋放。否則,其他線程將永遠(yuǎn)阻塞。好了,ReentrantLock我們就簡(jiǎn)單介紹到這里,接下來(lái)我們通過(guò)ReentrantLock來(lái)解決前面賣(mài)票線程的線程同步(安全)問(wèn)題,代碼如下:
  1. package com.zejian.test;  
  2. import java.util.concurrent.locks.Lock;  
  3. import java.util.concurrent.locks.ReentrantLock;  
  4. /** 
  5.  * @author zejian 
  6.  * @time 2016年3月12日 下午2:55:42 
  7.  * @decrition 模擬賣(mài)票線程 
  8.  */  
  9. public class Ticket implements Runnable  
  10. {  
  11.     //創(chuàng)建鎖對(duì)象  
  12.     private Lock ticketLock = new ReentrantLock();  
  13.     //當(dāng)前擁有的票數(shù)  
  14.     private  int num = 100;  
  15.     public void run()  
  16.     {  
  17.         while(true)  
  18.         {         
  19.                 ticketLock.lock();//獲取鎖  
  20.                 if(num>0)  
  21.                 {  
  22.                   
  23.                     try{  
  24.                         Thread.sleep(10);  
  25.                         //輸出賣(mài)票信息  
  26.                         System.out.println(Thread.currentThread().getName()+".....sale...."+num--);  
  27.                     }catch (InterruptedException e){  
  28.                         Thread.currentThread().interrupt();//出現(xiàn)異常就中斷  
  29.                     }finally{  
  30.                         ticketLock.unlock();//釋放鎖  
  31.                     }     
  32.                 }  
  33.         }  
  34.     }  
  35. }  
TicketDemo類(lèi)無(wú)需變化,運(yùn)行結(jié)果正常(太多不貼了),線程安全問(wèn)題就此解決。
2.2通過(guò)synchronied關(guān)鍵字的方式解決線程安全問(wèn)題
在Java中內(nèi)置了語(yǔ)言級(jí)的同步原語(yǔ)-synchronized,這個(gè)可以大大簡(jiǎn)化了Java中多線程同步的使用。從JAVA SE1.0開(kāi)始,java中的每一個(gè)對(duì)象都有一個(gè)內(nèi)部鎖,如果一個(gè)方法使用synchronized關(guān)鍵字進(jìn)行聲明,那么這個(gè)對(duì)象將保護(hù)整個(gè)方法,也就是說(shuō)調(diào)用該方法線程必須獲得內(nèi)部的對(duì)象鎖。
  1. public synchronized void method{  
  2.   //method body  
  3. }  
等價(jià)于
  1. private Lock ticketLock = new ReentrantLock();  
  2. public void method{  
  3.  ticketLock.lock();  
  4.  try{  
  5.   //.......  
  6.  }finally{  
  7.    ticketLock.unlock();  
  8.  }  
  9. }  
從這里可以看出使用synchronized關(guān)鍵字來(lái)編寫(xiě)代碼要簡(jiǎn)潔得多了。當(dāng)然,要理解這一代碼,我們必須知道每個(gè)對(duì)象有一個(gè)內(nèi)部鎖,并且該鎖有一個(gè)內(nèi)部條件。由鎖來(lái)管理那些試圖進(jìn)入synchronized方法的線程,由條件來(lái)管那些調(diào)用wait的線程(wait()/notifyAll/notify())。同時(shí)我們必須明白一旦有一個(gè)線程通過(guò)synchronied方法獲取到內(nèi)部鎖,該類(lèi)的所有synchronied方法或者代碼塊都無(wú)法被其他線程訪問(wèn)直到當(dāng)前線程釋放了內(nèi)部鎖。剛才上面說(shuō)的是同步方法,synchronized還有一種同步代碼塊的實(shí)現(xiàn)方式:
  1. Object obj = new Object();  
  2. synchronized(obj){  
  3.   //需要同步的代碼  
  4. }  
其中obj是對(duì)象鎖,可以是任意對(duì)象。那么我們就通過(guò)其中的一個(gè)方法來(lái)解決售票系統(tǒng)的線程同步問(wèn)題:
  1. class Ticket implements Runnable  
  2. {  
  3.     private  int num = 100;  
  4.     Object obj = new Object();  
  5.     public void run()  
  6.     {  
  7.         while(true)  
  8.         {  
  9.             synchronized(obj)  
  10.             {  
  11.                 if(num>0)  
  12.                 {  
  13.                     try{Thread.sleep(10);}catch (InterruptedException e){}  
  14.                       
  15.                     System.out.println(Thread.currentThread().getName()+".....sale...."+num--);  
  16.                 }  
  17.             }  
  18.         }  
  19.     }  
  20. }  
嗯,同步代碼塊解決,運(yùn)行結(jié)果也正常。到此同步問(wèn)題也就解決了,當(dāng)然代碼同步也是要犧牲效率為前提的:
同步的好處:解決了線程的安全問(wèn)題。
同步的弊端:相對(duì)降低了效率,因?yàn)橥酵獾木€程的都會(huì)判斷同步鎖。
同步的前提:同步中必須有多個(gè)線程并使用同一個(gè)鎖。
3.線程間的通信機(jī)制
線程開(kāi)始運(yùn)行,擁有自己的棧空間,但是如果每個(gè)運(yùn)行中的線程,如果僅僅是孤立地運(yùn)行,那么沒(méi)有一點(diǎn)兒價(jià)值,或者是價(jià)值很小,如果多線程能夠相互配合完成工作的話,這將帶來(lái)巨大的價(jià)值,這也就是線程間的通信啦。在java中多線程間的通信使用的是等待/通知機(jī)制來(lái)實(shí)現(xiàn)的。
3.1synchronied關(guān)鍵字等待/通知機(jī)制:是指一個(gè)線程A調(diào)用了對(duì)象O的wait()方法進(jìn)入等待狀態(tài),而另一個(gè)線程B調(diào)用了對(duì)象O的notify()或者notifyAll()方法,線程A收到通知后從對(duì)象O的wait()方法返回,進(jìn)而執(zhí)行后續(xù)操作。上述的兩個(gè)線程通過(guò)對(duì)象O來(lái)完成交互,而對(duì)象上的wait()和notify()/notifyAll()的關(guān)系就如同開(kāi)關(guān)信號(hào)一樣,用來(lái)完成等待方和通知方之間的交互工作。
等待/通知機(jī)制主要是用到的函數(shù)方法是notify()/notifyAll(),wait()/wait(long),wait(long,int),這些方法在上一篇文章都有說(shuō)明過(guò),這里就不重復(fù)了。當(dāng)然這是針對(duì)synchronied關(guān)鍵字修飾的函數(shù)或代碼塊,因?yàn)橐褂?/span>notify()/notifyAll(),wait()/wait(long),wait(long,int)這些方法的前提是對(duì)調(diào)用對(duì)象加鎖,也就是說(shuō)只能在同步函數(shù)或者同步代碼塊中使用。
3.2條件對(duì)象的等待/通知機(jī)制:所謂的條件對(duì)象也就是配合前面我們分析的Lock鎖對(duì)象,通過(guò)鎖對(duì)象的條件對(duì)象來(lái)實(shí)現(xiàn)等待/通知機(jī)制。那么條件對(duì)象是怎么創(chuàng)建的呢?
  1. //創(chuàng)建條件對(duì)象  
  2. Condition conditionObj=ticketLock.newCondition();  
就這樣我們創(chuàng)建了一個(gè)條件對(duì)象。注意這里返回的對(duì)象是與該鎖(ticketLock)相關(guān)的條件對(duì)象。下面是條件對(duì)象的API:
方法函數(shù)方法對(duì)應(yīng)的描述                                                                                                                                                                                    
void await()將該線程放到條件等待池中(對(duì)應(yīng)wait()方法)
void signalAll()解除該條件等待池中所有線程的阻塞狀態(tài)(對(duì)應(yīng)notifyAll()方法)
void signal()從該條件的等待池中隨機(jī)地選擇一個(gè)線程,解除其阻塞狀態(tài)(對(duì)應(yīng)notify()方法)
上述方法的過(guò)程分析:一個(gè)線程A調(diào)用了條件對(duì)象的await()方法進(jìn)入等待狀態(tài),而另一個(gè)線程B調(diào)用了條件對(duì)象的signal()或者signalAll()方法,線程A收到通知后從條件對(duì)象的await()方法返回,進(jìn)而執(zhí)行后續(xù)操作。上述的兩個(gè)線程通過(guò)條件對(duì)象來(lái)完成交互,而對(duì)象上的await()和signal()/signalAll()的關(guān)系就如同開(kāi)關(guān)信號(hào)一樣,用來(lái)完成等待方和通知方之間的交互工作。當(dāng)然這樣的操作都是必須基于對(duì)象鎖的,當(dāng)前線程只有獲取了鎖,才能調(diào)用該條件對(duì)象的await()方法,而調(diào)用后,當(dāng)前線程將縮放鎖。
這里有點(diǎn)要特別注意的是,上述兩種等待/通知機(jī)制中,無(wú)論是調(diào)用了signal()/signalAll()方法還是調(diào)用了notify()/notifyAll()方法并不會(huì)立即激活一個(gè)等待線程。它們僅僅都只是解除等待線程的阻塞狀態(tài),以便這些線程可以在當(dāng)前線程解鎖或者退出同步方法后,通過(guò)爭(zhēng)奪CPU執(zhí)行權(quán)實(shí)現(xiàn)對(duì)對(duì)象的訪問(wèn)。到此,線程通信機(jī)制的概念分析完,我們下面通過(guò)生產(chǎn)者消費(fèi)者模式來(lái)實(shí)現(xiàn)等待/通知機(jī)制。
4.生產(chǎn)者消費(fèi)者模式
4.1單生產(chǎn)者單消費(fèi)者模式
顧名思義,就是一個(gè)線程消費(fèi),一個(gè)線程生產(chǎn)。我們先來(lái)看看等待/通知機(jī)制下的生產(chǎn)者消費(fèi)者模式:我們假設(shè)這樣一個(gè)場(chǎng)景,我們是賣(mài)北京烤鴨店鋪,我們現(xiàn)在只有一條生產(chǎn)線也只有一條消費(fèi)線,也就是說(shuō)只能生產(chǎn)線程生產(chǎn)完了,再通知消費(fèi)線程才能去賣(mài),如果消費(fèi)線程沒(méi)烤鴨了,就必須通知生產(chǎn)線程去生產(chǎn),此時(shí)消費(fèi)線程進(jìn)入等待狀態(tài)。在這樣的場(chǎng)景下,我們不僅要保證共享數(shù)據(jù)(烤鴨數(shù)量)的線程安全,而且還要保證烤鴨數(shù)量在消費(fèi)之前必須有烤鴨。下面我們通過(guò)java代碼來(lái)實(shí)現(xiàn):
北京烤鴨生產(chǎn)資源類(lèi)KaoYaResource:
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月12日 下午10:44:25 
  5.  * @decrition 烤鴨資源 
  6.  */  
  7. public class KaoYaResource {  
  8.       
  9.     private String name;  
  10.     private int count = 1;//烤鴨的初始數(shù)量  
  11.     private boolean flag = false;//判斷是否有需要線程等待的標(biāo)志  
  12.       
  13.     /** 
  14.      * 生產(chǎn)烤鴨 
  15.      */  
  16.     public synchronized void product(String name){  
  17.         if(flag){  
  18.             //此時(shí)有烤鴨,等待  
  19.             try {  
  20.                 this.wait();  
  21.             } catch (InterruptedException e) {  
  22.                 e.printStackTrace()  
  23. ;  
  24.             }  
  25.         }  
  26.         this.name=name+count;//設(shè)置烤鴨的名稱  
  27.         count++;  
  28.         System.out.println(Thread.currentThread().getName()+"...生產(chǎn)者..."+this.name);  
  29.         flag=true;//有烤鴨后改變標(biāo)志  
  30.         notifyAll();//通知消費(fèi)線程可以消費(fèi)了  
  31.     }  
  32.       
  33.     /** 
  34.      * 消費(fèi)烤鴨 
  35.      */  
  36.     public synchronized void consume(){  
  37.         if(!flag){//如果沒(méi)有烤鴨就等待  
  38.             try{this.wait();}catch(InterruptedException e){}  
  39.         }  
  40.         System.out.println(Thread.currentThread().getName()+"...消費(fèi)者........"+this.name);//消費(fèi)烤鴨1  
  41.         flag = false;  
  42.         notifyAll();//通知生產(chǎn)者生產(chǎn)烤鴨  
  43.     }  
  44. }  
在這個(gè)類(lèi)中我們有兩個(gè)synchronized的同步方法,一個(gè)是生產(chǎn)烤鴨的,一個(gè)是消費(fèi)烤鴨的,之所以需要同步是因?yàn)槲覀儾僮髁斯蚕頂?shù)據(jù)count,同時(shí)為了保證生產(chǎn)烤鴨后才能消費(fèi)也就是生產(chǎn)一只烤鴨后才能消費(fèi)一只烤鴨,我們使用了等待/通知機(jī)制,wait()和notify()。當(dāng)?shù)谝淮芜\(yùn)行生產(chǎn)現(xiàn)場(chǎng)時(shí)調(diào)用生產(chǎn)的方法,此時(shí)有一只烤鴨,即flag=false,無(wú)需等待,因此我們?cè)O(shè)置可消費(fèi)的烤鴨名稱然后改變flag=true,同時(shí)通知消費(fèi)線程可以消費(fèi)烤鴨了,即使此時(shí)生產(chǎn)線程再次搶到執(zhí)行權(quán),因?yàn)閒lag=true,所以生產(chǎn)線程會(huì)進(jìn)入等待阻塞狀態(tài),消費(fèi)線程被喚醒后就進(jìn)入消費(fèi)方法,消費(fèi)完成后,又改變標(biāo)志flag=false,通知生產(chǎn)線程可以生產(chǎn)烤鴨了.........以此循環(huán)。
生產(chǎn)消費(fèi)執(zhí)行類(lèi)Single_Producer_Consumer.java:
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月12日 下午10:29:12 
  5.  * @decrition 單生產(chǎn)者單消費(fèi)者模式 
  6.  */  
  7. public class Single_Producer_Consumer {  
  8.       
  9.     public static void main(String[] args)   
  10.     {  
  11.         KaoYaResource r = new KaoYaResource();  
  12.         Producer pro = new Producer(r);  
  13.         Consumer con = new Consumer(r);  
  14.         //生產(chǎn)者線程  
  15.         Thread t0 = new Thread(pro);  
  16.         //消費(fèi)者線程  
  17.         Thread t2 = new Thread(con);  
  18.         //啟動(dòng)線程  
  19.         t0.start();  
  20.         t2.start();  
  21.     }  
  22. }  
  23. /** 
  24.  * @author zejian 
  25.  * @time 2016年3月12日 下午11:02:22 
  26.  * @decrition 生產(chǎn)者線程 
  27.  */  
  28. class Producer implements Runnable  
  29. {  
  30.     private KaoYaResource r;  
  31.     Producer(KaoYaResource r)  
  32.     {  
  33.         this.r = r;  
  34.     }  
  35.     public void run()  
  36.     {  
  37.         while(true)  
  38.         {  
  39.             r.product("北京烤鴨");  
  40.         }  
  41.     }  
  42. }  
  43. /** 
  44.  * @author zejian 
  45.  * @time 2016年3月12日 下午11:02:05 
  46.  * @decrition 消費(fèi)者線程 
  47.  */  
  48. class Consumer implements Runnable  
  49. {  
  50.     private KaoYaResource r;  
  51.     Consumer(KaoYaResource r)  
  52.     {  
  53.         this.r = r;  
  54.     }  
  55.     public void run()  
  56.     {  
  57.         while(true)  
  58.         {  
  59.             r.consume();  
  60.         }  
  61.     }  
  62. }  
在這個(gè)類(lèi)中我們創(chuàng)建兩個(gè)線程,一個(gè)是消費(fèi)者線程,一個(gè)是生產(chǎn)者線程,我們分別開(kāi)啟這兩個(gè)線程用于不斷的生產(chǎn)消費(fèi),運(yùn)行結(jié)果如下:

很顯然的情況就是生產(chǎn)一只烤鴨然后就消費(fèi)一只烤鴨。運(yùn)行情況完全正常,嗯,這就是單生產(chǎn)者單消費(fèi)者模式。上面使用的是synchronized關(guān)鍵字的方式實(shí)現(xiàn)的,那么接下來(lái)我們使用對(duì)象鎖的方式實(shí)現(xiàn):KaoYaResourceByLock.java
  1. package com.zejian.test;  
  2. import java.util.concurrent.locks.Condition;  
  3. import java.util.concurrent.locks.Lock;  
  4. import java.util.concurrent.locks.ReentrantLock;  
  5. /** 
  6.  * @author zejian 
  7.  * @time 2016年3月13日 上午9:55:35 
  8.  * @decrition 通過(guò)對(duì)象鎖的方式來(lái)實(shí)現(xiàn)等待/通知機(jī)制 
  9.  */  
  10. public class KaoyaResourceByLock {  
  11.       
  12.     private String name;  
  13.     private int count = 1;//烤鴨的初始數(shù)量  
  14.     private boolean flag = false;//判斷是否有需要線程等待的標(biāo)志  
  15.     //創(chuàng)建一個(gè)鎖對(duì)象  
  16.     private Lock resourceLock=new ReentrantLock();  
  17.     //創(chuàng)建條件對(duì)象  
  18.     private Condition condition= resourceLock.newCondition();  
  19.     /** 
  20.      * 生產(chǎn)烤鴨 
  21.      */  
  22.     public  void product(String name){  
  23.         resourceLock.lock();//先獲取鎖  
  24.         try{  
  25.             if(flag){  
  26.                 try {  
  27.                     condition.await();  
  28.                 } catch (InterruptedException e) {  
  29.                     e.printStackTrace();  
  30.                 }  
  31.             }  
  32.             this.name=name+count;//設(shè)置烤鴨的名稱  
  33.             count++;  
  34.             System.out.println(Thread.currentThread().getName()+"...生產(chǎn)者..."+this.name);  
  35.             flag=true;//有烤鴨后改變標(biāo)志  
  36.             condition.signalAll();//通知消費(fèi)線程可以消費(fèi)了  
  37.         }finally{  
  38.             resourceLock.unlock();  
  39.         }     
  40.     }  
  41.       
  42.     /** 
  43.      * 消費(fèi)烤鴨 
  44.      */  
  45.     public  void consume(){  
  46.         resourceLock.lock();  
  47.         try{  
  48.         if(!flag){//如果沒(méi)有烤鴨就等待  
  49.             try{condition.await();}catch(InterruptedException e){}  
  50.         }  
  51.         System.out.println(Thread.currentThread().getName()+"...消費(fèi)者........"+this.name);//消費(fèi)烤鴨1  
  52.         flag = false;  
  53.         condition.signalAll();//通知生產(chǎn)者生產(chǎn)烤鴨  
  54.         }finally{  
  55.             resourceLock.unlock();  
  56.         }  
  57.     }  
  58. }  
代碼變化不大,我們通過(guò)對(duì)象鎖的方式去實(shí)現(xiàn),首先要?jiǎng)?chuàng)建一個(gè)對(duì)象鎖,我們這里使用的重入鎖ReestrantLock類(lèi),然后通過(guò)手動(dòng)設(shè)置lock()和unlock()的方式去獲取鎖以及釋放鎖。為了實(shí)現(xiàn)等待/通知機(jī)制,我們還必須通過(guò)鎖對(duì)象去創(chuàng)建一個(gè)條件對(duì)象Condition,然后通過(guò)鎖對(duì)象的await()和signalAll()方法去實(shí)現(xiàn)等待以及通知操作。Single_Producer_Consumer.java代碼替換一下資源類(lèi)即可,運(yùn)行結(jié)果就不貼了,有興趣自行操作即可。
4.2多生產(chǎn)者多消費(fèi)者模式
分析完了單生產(chǎn)者單消費(fèi)者模式,我們?cè)賮?lái)聊聊多生產(chǎn)者多消費(fèi)者模式,也就是多條生產(chǎn)線程配合多條消費(fèi)線程。既然這樣的話我們先把上面的代碼Single_Producer_Consumer.java類(lèi)修改成新類(lèi),大部分代碼不變,僅新增2條線程去跑,一條t1的生產(chǎn)  共享資源類(lèi)KaoYaResource不作更改,代碼如下:
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月13日 上午10:35:05 
  5.  * @decrition 多生產(chǎn)者多消費(fèi)者模式 
  6.  */  
  7. public class Mutil_Producer_Consumer {  
  8.       
  9.     public static void main(String[] args)   
  10.     {  
  11.         KaoYaResource r = new KaoYaResource();  
  12.         Mutil_Producer pro = new Mutil_Producer(r);  
  13.         Mutil_Consumer con = new Mutil_Consumer(r);  
  14.         //生產(chǎn)者線程  
  15.         Thread t0 = new Thread(pro);  
  16.         Thread t1 = new Thread(pro);  
  17.         //消費(fèi)者線程  
  18.         Thread t2 = new Thread(con);  
  19.         Thread t3 = new Thread(con);  
  20.         //啟動(dòng)線程  
  21.         t0.start();  
  22.         t1.start();  
  23.         t2.start();  
  24.         t3.start();  
  25.     }  
  26. }  
  27. /** 
  28.  * @author zejian 
  29.  * @time 2016年3月12日 下午11:02:22 
  30.  * @decrition 生產(chǎn)者線程 
  31.  */  
  32. class Mutil_Producer implements Runnable  
  33. {  
  34.     private KaoYaResource r;  
  35.     Mutil_Producer(KaoYaResource r)  
  36.     {  
  37.         this.r = r;  
  38.     }  
  39.     public void run()  
  40.     {  
  41.         while(true)  
  42.         {  
  43.             r.product("北京烤鴨");  
  44.         }  
  45.     }  
  46. }  
  47. /** 
  48.  * @author zejian 
  49.  * @time 2016年3月12日 下午11:02:05 
  50.  * @decrition 消費(fèi)者線程 
  51.  */  
  52. class Mutil_Consumer implements Runnable  
  53. {  
  54.     private KaoYaResource r;  
  55.     Mutil_Consumer(KaoYaResource r)  
  56.     {  
  57.         this.r = r;  
  58.     }  
  59.     public void run()  
  60.     {  
  61.         while(true)  
  62.         {  
  63.             r.consume();  
  64.         }  
  65.     }  
  66. }  
就多了兩條線程,我們運(yùn)行代碼看看,結(jié)果如下:

不對(duì)呀,我們才生產(chǎn)一只烤鴨,怎么就被消費(fèi)了3次啊,有的烤鴨生產(chǎn)了也沒(méi)有被消費(fèi)啊?難道共享數(shù)據(jù)源沒(méi)有進(jìn)行線程同步?我們?cè)倏纯粗暗腒aoYaResource.java
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月12日 下午10:44:25 
  5.  * @decrition 烤鴨資源 
  6.  */  
  7. public class KaoYaResource {  
  8.       
  9.     private String name;  
  10.     private int count = 1;//烤鴨的初始數(shù)量  
  11.     private boolean flag = false;//判斷是否有需要線程等待的標(biāo)志  
  12.       
  13.     /** 
  14.      * 生產(chǎn)烤鴨 
  15.      */  
  16.     public synchronized void product(String name){  
  17.         if(flag){  
  18.             //此時(shí)有烤鴨,等待  
  19.             try {  
  20.                 this.wait();  
  21.             } catch (InterruptedException e) {  
  22.                 e.printStackTrace();  
  23.             }  
  24.         }  
  25.         this.name=name+count;//設(shè)置烤鴨的名稱  
  26.         count++;  
  27.         System.out.println(Thread.currentThread().getName()+"...生產(chǎn)者..."+this.name);  
  28.         flag=true;//有烤鴨后改變標(biāo)志  
  29.         notifyAll();//通知消費(fèi)線程可以消費(fèi)了  
  30.     }  
  31.       
  32.     /** 
  33.      * 消費(fèi)烤鴨 
  34.      */  
  35.     public synchronized void consume(){  
  36.         if(!flag){//如果沒(méi)有烤鴨就等待  
  37.             try{this.wait();}catch(InterruptedException e){}  
  38.         }  
  39.         System.out.println(Thread.currentThread().getName()+"...消費(fèi)者........"+this.name);//消費(fèi)烤鴨1  
  40.         flag = false;  
  41.         notifyAll();//通知生產(chǎn)者生產(chǎn)烤鴨  
  42.     }  
  43. }  
共享數(shù)據(jù)count的獲取方法都進(jìn)行synchronized關(guān)鍵字同步了呀!那怎么還會(huì)出現(xiàn)數(shù)據(jù)混亂的現(xiàn)象?。?/div>
分析:確實(shí),我們對(duì)共享數(shù)據(jù)也采用了同步措施,而且也應(yīng)用了等待/通知機(jī)制,但是這樣的措施只在單生產(chǎn)者單消費(fèi)者的情況下才能正確應(yīng)用,但從運(yùn)行結(jié)果來(lái)看,我們之前的單生產(chǎn)者單消費(fèi)者安全處理措施就不太適合多生產(chǎn)者多消費(fèi)者的情況了。那么問(wèn)題出在哪里?可以明確的告訴大家,肯定是在資源共享類(lèi),下面我們就來(lái)分析問(wèn)題是如何出現(xiàn),又該如何解決?直接上圖

解決后的資源代碼如下只將if改為了while:
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月12日 下午10:44:25 
  5.  * @decrition 烤鴨資源 
  6.  */  
  7. public class KaoYaResource {  
  8.       
  9.     private String name;  
  10.     private int count = 1;//烤鴨的初始數(shù)量  
  11.     private boolean flag = false;//判斷是否有需要線程等待的標(biāo)志  
  12.     /** 
  13.      * 生產(chǎn)烤鴨 
  14.      */  
  15.     public synchronized void product(String name){  
  16.         while(flag){  
  17.             //此時(shí)有烤鴨,等待  
  18.             try {  
  19.                 this.wait();  
  20.             } catch (InterruptedException e) {  
  21.                 e.printStackTrace();  
  22.             }  
  23.         }  
  24.         this.name=name+count;//設(shè)置烤鴨的名稱  
  25.         count++;  
  26.         System.out.println(Thread.currentThread().getName()+"...生產(chǎn)者..."+this.name);  
  27.         flag=true;//有烤鴨后改變標(biāo)志  
  28.         notifyAll();//通知消費(fèi)線程可以消費(fèi)了  
  29.     }  
  30.       
  31.     /** 
  32.      * 消費(fèi)烤鴨 
  33.      */  
  34.     public synchronized void consume(){  
  35.         while(!flag){//如果沒(méi)有烤鴨就等待  
  36.             try{this.wait();}catch(InterruptedException e){}  
  37.         }  
  38.         System.out.println(Thread.currentThread().getName()+"...消費(fèi)者........"+this.name);//消費(fèi)烤鴨1  
  39.         flag = false;  
  40.         notifyAll();//通知生產(chǎn)者生產(chǎn)烤鴨  
  41.     }  
  42. }  
運(yùn)行代碼,結(jié)果如下:

到此,多消費(fèi)者多生產(chǎn)者模式也完成,不過(guò)上面用的是synchronied關(guān)鍵字實(shí)現(xiàn)的,而鎖對(duì)象的解決方法也一樣將之前單消費(fèi)者單生產(chǎn)者的資源類(lèi)中的if判斷改為while判斷即可代碼就不貼了哈。不過(guò)下面我們將介紹一種更有效的鎖對(duì)象解決方法,我們準(zhǔn)備使用兩組條件對(duì)象(Condition也稱為監(jiān)視器)來(lái)實(shí)現(xiàn)等待/通知機(jī)制,也就是說(shuō)通過(guò)已有的鎖獲取兩組監(jiān)視器,一組監(jiān)視生產(chǎn)者,一組監(jiān)視消費(fèi)者。有了前面的分析這里我們直接上代碼:
  1. package com.zejian.test;  
  2. import java.util.concurrent.locks.Condition;  
  3. import java.util.concurrent.locks.Lock;  
  4. import java.util.concurrent.locks.ReentrantLock;  
  5. /** 
  6.  * @author zejian 
  7.  * @time 2016年3月13日 下午12:03:27 
  8.  * @decrition 通過(guò)已有的鎖獲取兩組監(jiān)視器,一組監(jiān)視生產(chǎn)者,一組監(jiān)視消費(fèi)者。 
  9.  */  
  10. public class ResourceBy2Condition {  
  11.     private String name;  
  12.     private int count = 1;  
  13.     private boolean flag = false;  
  14.       
  15.     //創(chuàng)建一個(gè)鎖對(duì)象。  
  16.     Lock lock = new ReentrantLock();  
  17.       
  18.     //通過(guò)已有的鎖獲取兩組監(jiān)視器,一組監(jiān)視生產(chǎn)者,一組監(jiān)視消費(fèi)者。  
  19.     Condition producer_con = lock.newCondition();  
  20.     Condition consumer_con = lock.newCondition();  
  21.       
  22.     /** 
  23.      * 生產(chǎn) 
  24.      * @param name 
  25.      */  
  26.     public  void product(String name)  
  27.     {  
  28.         lock.lock();  
  29.         try  
  30.         {  
  31.             while(flag){  
  32.                 try{producer_con.await();}catch(InterruptedException e){}  
  33.             }  
  34.             this.name = name + count;  
  35.             count++;  
  36.             System.out.println(Thread.currentThread().getName()+"...生產(chǎn)者5.0..."+this.name);  
  37.             flag = true;  
  38. //          notifyAll();  
  39. //          con.signalAll();  
  40.             consumer_con.signal();//直接喚醒消費(fèi)線程  
  41.         }  
  42.         finally  
  43.         {  
  44.             lock.unlock();  
  45.         }  
  46.     }  
  47.       
  48.     /** 
  49.      * 消費(fèi) 
  50.      */  
  51.     public  void consume()  
  52.     {  
  53.         lock.lock();  
  54.         try  
  55.         {  
  56.             while(!flag){  
  57.                 try{consumer_con.await();}catch(InterruptedException e){}  
  58.             }  
  59.             System.out.println(Thread.currentThread().getName()+"...消費(fèi)者.5.0......."+this.name);//消費(fèi)烤鴨1  
  60.             flag = false;  
  61. //          notifyAll();  
  62. //          con.signalAll();  
  63.             producer_con.signal();//直接喚醒生產(chǎn)線程  
  64.         }  
  65.         finally  
  66.         {  
  67.             lock.unlock();  
  68.         }  
  69.     }  
  70. }  
從代碼中可以看到,我們創(chuàng)建了producer_con 和consumer_con兩個(gè)條件對(duì)象,分別用于監(jiān)聽(tīng)生產(chǎn)者線程和消費(fèi)者線程,在product()方法中,我們獲取到鎖后,
如果此時(shí)flag為true的話,也就是此時(shí)還有烤鴨未被消費(fèi),因此生產(chǎn)線程需要等待,所以我們調(diào)用生產(chǎn)線程的監(jiān)控producer_con的
await()的方法進(jìn)入阻塞等待池;但如果此時(shí)的flag為false的話,就說(shuō)明烤鴨已經(jīng)消費(fèi)完,需要生產(chǎn)線程去生產(chǎn)烤鴨,那么生產(chǎn)線程將進(jìn)行烤
鴨生產(chǎn)并通過(guò)消費(fèi)線程的監(jiān)控器consumer_con的signal()方法去通知消費(fèi)線程對(duì)烤鴨進(jìn)行消費(fèi)。consume()方法也是同樣的道理,這里就不
過(guò)多分析了。我們可以發(fā)現(xiàn)這種方法比我們之前的synchronized同步方法或者是單監(jiān)視器的鎖對(duì)象都來(lái)得高效和方便些,之前都是使用
notifyAll()和signalAll()方法去喚醒池中的線程,然后讓池中的線程又進(jìn)入 競(jìng)爭(zhēng)隊(duì)列去搶占CPU資源,這樣不僅喚醒了無(wú)關(guān)的線程而且又讓全
部線程進(jìn)入了競(jìng)爭(zhēng)隊(duì)列中,而我們最后使用兩種監(jiān)聽(tīng)器分別監(jiān)聽(tīng)生產(chǎn)者線程和消費(fèi)者線程,這樣的方式恰好解決前面兩種方式的問(wèn)題所在,
我們每次喚醒都只是生產(chǎn)者線程或者是消費(fèi)者線程而不會(huì)讓兩者同時(shí)喚醒,這樣不就能更高效得去執(zhí)行程序了嗎?好了,到此多生產(chǎn)者多消
費(fèi)者模式也分析完畢。
5.線程死鎖
現(xiàn)在我們?cè)賮?lái)討論一下線程死鎖問(wèn)題,從上面的分析,我們知道鎖是個(gè)非常有用的工具,運(yùn)用的場(chǎng)景非常多,因?yàn)樗褂闷饋?lái)非常簡(jiǎn)單,而
且易于理解。但同時(shí)它也會(huì)帶來(lái)一些不必要的麻煩,那就是可能會(huì)引起死鎖,一旦產(chǎn)生死鎖,就會(huì)造成系統(tǒng)功能不可用。我們先通過(guò)一個(gè)例
子來(lái)分析,這個(gè)例子會(huì)引起死鎖,使得線程t1和線程t2互相等待對(duì)方釋放鎖。
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月13日 下午2:45:52 
  5.  * @decrition 死鎖示例 
  6.  */  
  7. public class DeadLockDemo {  
  8.       
  9.     private static String A="A";  
  10.     private static String B="B";  
  11.               
  12.     public static void main(String[] args) {  
  13.         DeadLockDemo deadLock=new DeadLockDemo();  
  14.         while(true){  
  15.             deadLock.deadLock();  
  16.         }  
  17.     }  
  18.       
  19.     private void deadLock(){  
  20.         Thread t1=new Thread(new Runnable(){  
  21.             @SuppressWarnings("static-access")  
  22.             @Override  
  23.             public void run() {  
  24.                 synchronized (A) {  
  25.                     try {  
  26.                         Thread.currentThread().sleep(2000);  
  27.                     } catch (InterruptedException e) {  
  28.                         e.printStackTrace();  
  29.                     }  
  30.                 }  
  31.                 synchronized(B){  
  32.                     System.out.println("1");  
  33.                 }  
  34.             }  
  35.         });  
  36.           
  37.         Thread t2 =new Thread(new Runnable() {    
  38.             @Override  
  39.             public void run() {  
  40.                 synchronized (B) {  
  41.                     synchronized (A) {  
  42.                         System.out.println("2");  
  43.                     }  
  44.                 }  
  45.             }  
  46.         });  
  47.           
  48.         //啟動(dòng)線程  
  49.         t1.start();  
  50.         t2.start();  
  51.     }     
  52. }  
同步嵌套是產(chǎn)生死鎖的常見(jiàn)情景,從上面的代碼中我們可以看出,當(dāng)t1線程拿到鎖A后,睡眠2秒,此時(shí)線程t2剛好拿到了B鎖,接著要獲取A鎖,但是此時(shí)A鎖正好被t1線程持有,因此只能等待t1線程釋放鎖A,但遺憾的是在t1線程內(nèi)又要求獲取到B鎖,而B(niǎo)鎖此時(shí)又被t2線程持有,到此結(jié)果就是t1線程拿到了鎖A同時(shí)在等待t2線程釋放鎖B,而t2線程獲取到了鎖B也同時(shí)在等待t1線程釋放鎖A,彼此等待也就造成了線程死鎖問(wèn)題。雖然我們現(xiàn)實(shí)中一般不會(huì)向上面那么寫(xiě)出那樣的代碼,但是有些更為復(fù)雜的場(chǎng)景中,我們可能會(huì)遇到這樣的問(wèn)題,比如t1拿了鎖之后,因?yàn)橐恍┊惓G闆r沒(méi)有釋放鎖(死循環(huán)),也可能t1拿到一個(gè)數(shù)據(jù)庫(kù)鎖,釋放鎖的時(shí)候拋出了異常,沒(méi)有釋放等等,所以我們應(yīng)該在寫(xiě)代碼的時(shí)候多考慮死鎖的情況,這樣才能有效預(yù)防死鎖程序的出現(xiàn)。下面我們介紹一下避免死鎖的幾個(gè)常見(jiàn)方法:
1.避免一個(gè)線程同時(shí)獲取多個(gè)鎖。
2.避免在一個(gè)資源內(nèi)占用多個(gè) 資源,盡量保證每個(gè)鎖只占用一個(gè)資源。
3.嘗試使用定時(shí)鎖,使用tryLock(timeout)來(lái)代替使用內(nèi)部鎖機(jī)制。
4.對(duì)于數(shù)據(jù)庫(kù)鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫(kù)連接里,否則會(huì)出現(xiàn)解鎖失敗的情況。
5.避免同步嵌套的發(fā)生
6.Thread.join()
如果一個(gè)線程A執(zhí)行了thread.join()語(yǔ)句,其含義是:當(dāng)前線程A等待thread線程終止之后才能從thread.join()返回。線程Thread除了提供join()方法之外,還提供了join(long millis)和join(long millis,int nanos)兩個(gè)具備超時(shí)特性的方法。這兩個(gè)超時(shí)的方法表示,如果線程在給定的超時(shí)時(shí)間里沒(méi)有終止,那么將會(huì)從該超時(shí)方法中返回。下面給出一個(gè)例子,創(chuàng)建10個(gè)線程,編號(hào)0~9,每個(gè)線程調(diào)用錢(qián)一個(gè)線程的join()方法,也就是線程0結(jié)束了,線程1才能從join()方法中返回,而0需要等待main線程結(jié)束。
  1. package com.zejian.test;  
  2. /** 
  3.  * @author zejian 
  4.  * @time 2016年3月13日 下午4:10:03 
  5.  * @decrition join案例 
  6.  */  
  7. public class JoinDemo {  
  8.       
  9.     public static void main(String[] args) {  
  10.         Thread previous = Thread.currentThread();  
  11.         for(int i=0;i<10;i++){  
  12.             //每個(gè)線程擁有前一個(gè)線程的引用。需要等待前一個(gè)線程終止,才能從等待中返回  
  13.             Thread thread=new Thread(new Domino(previous),String.valueOf(i));  
  14.             thread.start();  
  15.             previous=thread;  
  16.         }  
  17.         System.out.println(Thread.currentThread().getName()+" 線程結(jié)束");  
  18.     }  
  19. }  
  20. class Domino implements Runnable{  
  21.     private Thread thread;  
  22.     public Domino(Thread thread){  
  23.         this.thread=thread;  
  24.     }  
  25.       
  26.     @Override  
  27.     public void run() {  
  28.         try {  
  29.             thread.join();  
  30.         } catch (InterruptedException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.         System.out.println(Thread.currentThread().getName()+" 線程結(jié)束");  
  34.     }  
  35.       
  36. }  
好了,到此本篇結(jié)束。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多