乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

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

       沙門空海 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)看一段賣票系統(tǒng)的代碼,然后再分析這個(gè)問(wèn)題:
      1. package com.zejian.test;  
      2. /** 
      3.  * @author zejian 
      4.  * @time 2016年3月12日 下午2:55:42 
      5.  * @decrition 模擬賣票線程 
      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.                     //輸出賣票信息  
      19.                     System.out.println(Thread.currentThread().getName()+".....sale...."+num--);  
      20.                 }  
      21.         }  
      22.     }  
      23. }  
      上面是賣票線程類,下來(lái)再來(lái)看看執(zhí)行類:
      1. package com.zejian.test;  
      2. /**  
      3.  * @author zejian  
      4.  * @time 2016年3月12日 下午2:54:18  
      5.  * @decrition 模擬賣票系統(tǒng),該案例只考慮單方面賣票,其他情況暫時(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í)賣票  
      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í)賣出了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è)線程類Ticket,在這個(gè)類中我們又聲明了一個(gè)成員變量num也就是票的數(shù)量,然后我們通過(guò)run方法不斷的去獲取票數(shù)并輸出,最后我們?cè)谕獠款怲icketDemo中創(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)我們分別介紹這兩種方法
      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)共享資源,比如讀寫鎖,后面我們會(huì)分析)。在Lock接口出現(xiàn)之前,java程序是靠synchronized關(guān)鍵字(后面分析)實(shí)現(xiàn)鎖功能的,而JAVA SE5.0之后并發(fā)包中新增了Lock接口用來(lái)實(shí)現(xiàn)鎖的功能,它提供了與synchronized關(guān)鍵字類似的同步功能,只是在使用時(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)子類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類似功能:
      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)解決前面賣票線程的線程同步(安全)問(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 模擬賣票線程 
      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.                         //輸出賣票信息  
      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類無(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開始,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)編寫代碼要簡(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)部鎖,該類的所有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ī)制
      線程開始運(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)系就如同開關(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)系就如同開關(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)景,我們是賣北京烤鴨店鋪,我們現(xiàn)在只有一條生產(chǎn)線也只有一條消費(fèi)線,也就是說(shuō)只能生產(chǎn)線程生產(chǎn)完了,再通知消費(fè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)資源類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è)類中我們有兩個(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í)行類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è)類中我們創(chuàng)建兩個(gè)線程,一個(gè)是消費(fèi)者線程,一個(gè)是生產(chǎn)者線程,我們分別開啟這兩個(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類,然后通過(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代碼替換一下資源類即可,運(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類修改成新類,大部分代碼不變,僅新增2條線程去跑,一條t1的生產(chǎn)  共享資源類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)象啊?
      分析:確實(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)分析問(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)者的資源類中的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)聽生產(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)聽器分別監(jiān)聽生產(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)生死鎖的常見情景,從上面的代碼中我們可以看出,當(dāng)t1線程拿到鎖A后,睡眠2秒,此時(shí)線程t2剛好拿到了B鎖,接著要獲取A鎖,但是此時(shí)A鎖正好被t1線程持有,因此只能等待t1線程釋放鎖A,但遺憾的是在t1線程內(nèi)又要求獲取到B鎖,而B鎖此時(shí)又被t2線程持有,到此結(jié)果就是t1線程拿到了鎖A同時(shí)在等待t2線程釋放鎖B,而t2線程獲取到了鎖B也同時(shí)在等待t1線程釋放鎖A,彼此等待也就造成了線程死鎖問(wèn)題。雖然我們現(xiàn)實(shí)中一般不會(huì)向上面那么寫出那樣的代碼,但是有些更為復(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)該在寫代碼的時(shí)候多考慮死鎖的情況,這樣才能有效預(yù)防死鎖程序的出現(xiàn)。下面我們介紹一下避免死鎖的幾個(gè)常見方法:
      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)用錢一個(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)買等信息,謹(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)論公約

        類似文章 更多