一道編程題以下:java
實例化三個線程,一個線程打印a,一個線程打印b,一個線程打印c,三個線程同時執行,要求打印出10個連着的abc。編程
題目分析:安全
經過題意咱們能夠得出,本題須要咱們使用三個線程,三個線程分別會打印6次字符,關鍵是如何保證順序必定是abc...呢。因此此題須要同步機制來解決問題!多線程
令打印字符A的線程爲ThreadA,打印B的ThreadB,打印C的爲ThreadC。問題爲三線程間的同步喚醒操做,主要的目的就是使程序按ThreadA->ThreadB->ThreadC->ThreadA循環執行三個線程,所以本人整理出了三種方式來解決此問題。ide
最初給個錯誤示例:函數
public class MyABC { public static void main(String[] args) { MyRun run = new MyRun(); Thread a = new Thread(run, "A"); Thread b = new Thread(run, "B"); Thread c = new Thread(run, "C"); a.start(); b.start(); c.start(); } } class MyRun implements Runnable { private int total = 1; private boolean goA = true; private boolean goB = false; private boolean goC = false; @Override public void run() { while (total <= 10) { if (Thread.currentThread().getName().equals("A") && goA) { System.out.print(Thread.currentThread().getName()); // 可試着用sleep來模擬CPU分配時間,看看是什麼結果 // Thread.currentThread().sleep(100); // synchronized (this) { goB = true; goA = false; // } } else if (Thread.currentThread().getName().equals("B") && goB) { System.out.print(Thread.currentThread().getName()); // 可試着用sleep來模擬CPU分配時間,看看是什麼結果 // Thread.currentThread().sleep(100); goC = true; goB = false; } else if (Thread.currentThread().getName().equals("C") && goC) { System.out.print(Thread.currentThread().getName()); // 可試着用sleep來模擬CPU分配時間,看看是什麼結果 // Thread.currentThread().sleep(100); goA = true; goC = false; total++; } } } }
解析:性能
該代碼嚴格依賴各個標誌,一旦標誌變換順序被打亂,程序就崩了。線程的執行是由系統隨機調度的,也就是當線程A執行完gob=true時,頗有可能CPU被系統收回,此時B執行,C執行,當C執行到goa=true時,假設系統把C的CPU收回,分配給A,而A獲得CPU權限後繼續下一條代碼goa=false;此時標誌位亂了,程序沒法繼續正常執行。由於線程是隨機的,全部還有不少可能。。。別的不說,最少要在兩個狀態那裏加上synchronized 包裹this
下面給出正確的作法:atom
1、經過兩個鎖(不推薦,可讀性和安全性比較差)spa
(1)解法1:
/** * 基於兩個lock實現連續打印abcabc.... * @author lixiaoxi * */ public class TwoLockPrinter implements Runnable { // 打印次數 private static final int PRINT_COUNT = 10; // 前一個線程的打印鎖 private final Object fontLock; // 本線程的打印鎖 private final Object thisLock; // 打印字符 private final char printChar; public TwoLockPrinter(Object fontLock, Object thisLock, char printChar) { this.fontLock = fontLock; this.thisLock = thisLock; this.printChar = printChar; } @Override public void run() { // 連續打印PRINT_COUNT次 for (int i = 0; i < PRINT_COUNT; i++) { // 獲取前一個線程的打印鎖 synchronized (fontLock) { // 獲取本線程的打印鎖 synchronized (thisLock) { //打印字符 System.out.print(printChar); // 經過本線程的打印鎖喚醒後面的線程 // notify和notifyall都可,由於同一時刻只有一個線程在等待(由於每一個線程只有自身的鎖和上一個鎖,不會出現a,b,c同時搶一個鎖的狀況) thisLock.notify(); } // 不是最後一次則經過fontLock等待被喚醒 // 必需要加判斷,否則雖然可以打印10次,但10次後就會直接死鎖 //最後一次打印的時候線程A喚醒線程B,A線程等待;線程B喚醒線程C,線程B等待;線程C喚醒線程A,線程C等待(此時只有A線程不會被阻塞,線程BC會被阻塞) if(i < PRINT_COUNT - 1){ try { // 經過fontLock等待被喚醒 fontLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //若是不加if(i < PRINT_COUNT - 1)的條件能夠這麼作:由於線程A不阻塞,而線程B在等待A鎖,線程C在等待B鎖,因此這裏能夠經過線程A的notify()釋放A鎖,來喚醒B線程 synchronized (thisLock) { thisLock.notify(); } } } } public static void main(String[] args) throws InterruptedException { // 打印A線程的鎖 Object lockA = new Object(); // 打印B線程的鎖 Object lockB = new Object(); // 打印C線程的鎖 Object lockC = new Object(); // 打印a的線程 Thread threadA = new Thread(new TwoLockPrinter(lockC, lockA, 'A')); // 打印b的線程 Thread threadB = new Thread(new TwoLockPrinter(lockA, lockB, 'B')); // 打印c的線程 Thread threadC = new Thread(new TwoLockPrinter(lockB, lockC, 'C')); // 依次開啓a b c線程 threadA.start(); Thread.sleep(100); // 確保按順序A、B、C執行 threadB.start(); Thread.sleep(100); threadC.start(); Thread.sleep(100); } }
打印結果:
ABCABCABCABCABCABCABCABCABCABC
分析:
此解法爲了爲了肯定喚醒、等待的順序,每個線程必須同時持有兩個對象鎖,才能繼續執行。一個對象鎖是fontLock,就是前一個線程所持有的對象鎖,還有一個就是自身對象鎖thisLock。主要的思想就是,爲了控制執行的順序,必需要先持有fontLock鎖,也就是前一個線程要釋放掉前一個線程自身的對象鎖,當前線程再去申請自身對象鎖,二者兼備時打印,以後首先調用thisLock.notify()釋放自身對象鎖,喚醒下一個等待線程,再調用fontLock.wait()釋放prev對象鎖,暫停當前線程,等待再次被喚醒後進入循環。運行上述代碼,能夠發現三個線程循環打印ABC,共10次。程序運行的主要過程就是A線程最早運行,持有C,A對象鎖,後釋放A鎖,喚醒B。線程B等待A鎖,再申請B鎖,後打印B,再釋放B鎖,喚醒C,線程C等待B鎖,再申請C鎖,後打印C,再釋放C鎖,喚醒A。看起來彷佛沒什麼問題,但若是你仔細想一下,就會發現有問題,就是初始條件,三個線程按照A,B,C的順序來啓動,按照前面的思考,A喚醒B,B喚醒C,C再喚醒A。可是這種假設依賴於JVM中線程調度、執行的順序,因此須要手動控制他們三個的啓動順序,即Thread.Sleep(100)。
通俗點說:就是每一個對象只有一把鎖,即lockA 一把鎖,lockB一把鎖,lockC一把鎖,其中想進入同步代碼塊,執行打印,必需要具備兩把鎖,即threadA要執行打印,要具有lockC和lockA,threadB要執行打印,必須具有lockA和lockB,threadC要具有lockB和lockC,並且獲取兩把鎖的順序要一致,即獲取到第一把鎖以後,才能去爭取第二把鎖(若是不能獲取第一把鎖,那麼取爭取第二把鎖的機會也沒有,就不能執行run方法),線程A啓動以後,在main主線程執行sleep休眠以後,得到cpu時間片開始執行run方法,此時得到lockC以後,再得到lockA,進入同步代碼塊,此時執行threadA執行thisLock.notify();喚醒外面等待的lockA鎖的線程(這個時候B線程尚未執行,因此外面沒有線程等待lockA鎖,若是B線程啓動以後,在lockA鎖池中等待,那麼執行notify()方法,會當即喚醒threadB線程,去參與lockA鎖的爭奪,可是notify()只是喚醒,並不會釋放鎖(只有在執行完synchronized (thisLock)的代碼以後,才能釋放thisLock的鎖,很明顯執行了thisLock.notify(),synchronized (thisLock)鎖住的代碼就已經結束,因此thisLock.notify()會當即喚醒B線程,同時釋放lockB的鎖),只有在threadA調用fontLock.wait()以後(線程執行完同步代碼塊也會釋放鎖),會當即釋放自身持有的lockC鎖(可是此時lockA鎖並無釋放),並進入等待,等待持有lockC鎖的線程的喚醒,即threadC線程,其餘代碼執行過程分析省略...
注意:一個線程經過synchronized嵌套鎖住多個對象,而後在最裏層調用wait()函數,只釋放wait()函數關聯的鎖對象,而不是釋放線程當時持有的所有鎖(對象的wait()函數,只會釋放該對象的鎖,不影響其餘的鎖的狀態)。
(2)解法2:
/** * 基於兩個lock實現連續打印abcabc.... * @author lixiaoxi * */ public class MyThreadPrinter2 implements Runnable { private String name; private Object prev; private Object self; private MyThreadPrinter2(String name, Object prev, Object self) { this.name = name; this.prev = prev; this.self = self; } @Override public void run() { int count = 10; while (count > 0) { synchronized (prev) { synchronized (self) { System.out.print(name); count--; //通知等待self鎖的線程能夠搶鎖了,即喚醒等待self鎖的線程,即後一個線程,但此時並無釋放self的鎖,只有執行完synchronized (self) {...}代碼後纔會釋放self的鎖 self.notify(); } try { prev.wait();//當前線程持有的prev的鎖被釋放,同時當前線程進入等待被喚醒 } catch (InterruptedException e) { e.printStackTrace(); } } } //若是缺失下面這段代碼,那麼會有兩個線程沒法退出,由於輸出A的線程打印10次後,沒辦法調用notify()通知打印B的線程,以此內推 // synchronized (self) { // /** // *線程A被喚醒,進入最後一次打印A的時候,self.notify()會喚醒等待A鎖(self)的線程(即B線程),經過prev.wait()會釋放線程A持有的C鎖,當前線程A等待被喚醒 // *線程B被喚醒,進入最後一次打印B的時候,self.notify()會喚醒等待B鎖(self)的線程(即C線程),經過prev.wait()會釋放線程B持有的A鎖,此時線程B等待被喚醒 // *線程C被喚醒,進入最後一次打印C的時候,self.notify()會喚醒等待C鎖(self)的線程(即A線程),經過prev.wait()會釋放線程C持有的B鎖,此時線程C等待被喚醒 // *總結:若是缺失這段代碼,線程A會被喚醒,可是BC會阻塞,造成死鎖 // *加入這段代碼的效果是:最後一次線程C在執行了prev.wait()以後,會釋放自身持有的A鎖,此時線程A被喚醒(B,C在等待被喚醒),繼續執行,進入到while(){}下面的synchronized (self) {} // *此時經過self.notify();會喚醒等待A鎖的線程(B線程),在線程A執行完run()方法後,A線程會釋放自身持有的A鎖,此時B線程搶到A鎖,繼續執行while後的同步代碼塊,同理B線程會喚醒C線程 // */ // self.notify(); // } // System.out.print(Thread.currentThread().getName()+"---執行完畢"); } public static void main(String[] args) throws Exception { Object a = new Object(); Object b = new Object(); Object c = new Object(); MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a); MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b); MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c); new Thread(pa).start(); Thread.sleep(10); new Thread(pb).start(); Thread.sleep(10); new Thread(pc).start(); } }
先來解釋一下其總體思路,從大的方向上來說,該問題爲三線程間的同步喚醒操做,主要的目的就是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中線程調度、執行的順序。具體來講就是,在main主線程啓動ThreadA後,須要在ThreadA執行完,在prev.wait()等待時,再切回線程啓動ThreadB,ThreadB執行完,在prev.wait()等待時,再切回主線程,啓動ThreadC,只有JVM按照這個線程運行順序執行,才能保證輸出的結果是正確的。而這依賴於JVM的具體實現。
考慮一種狀況,以下:若是主線程在啓動A後,在執行A的同步代碼塊的過程當中又切回主線程(假設在代碼self.notify以前就被切回主線程),啓動了ThreadB,ThreadC,以後,因爲ThreadA還沒有執行self.notify,也就是ThreadB須要在synchronized(prev)處等待,而ThreadB由於等待prev的鎖(A鎖),而沒法進入synchronized (self),因此線程B此時沒有持有自身的B鎖(self),而這時C卻調用synchronized(prev)獲取了對b的對象鎖。這樣,在A調用完後,同時ThreadB獲取了prev也就是a的對象鎖,而ThreadC持有B鎖,ThreadA也釋放了C鎖,此時ThreadC的執行條件就已經知足了,會打印C,以後釋放c,及b的對象鎖,這時ThreadB具有了運行條件,會打印B,也就是循環變成了ACBACB了。這種狀況,能夠經過在run中主動釋放CPU,來進行模擬(即在self.notify以前使用Thread.sleep(1)便可讓出cpu時間片,讓其餘線程執行)。爲了不這種與JVM調度有關的不肯定性。須要讓A,B,C三個線程以肯定的順序啓動,在線程A和線程B啓動以後,各加入:Thread.sleep(10);
(3)用concurrent包實現的一樣的邏輯代碼
/** * 基於兩個lock實現連續打印abcabc.... concurrent的邏輯代碼 * * @author lixiaoxi * */ public class MyThreadPrinter2 implements Runnable { private String name; private ReentrantLock prev; private ReentrantLock self; private Condition prevCondition; private Condition selfCondition; private MyThreadPrinter2(String name, ReentrantLock prev, Condition prevCondition, ReentrantLock self, Condition selfCondition) { this.name = name; this.prev = prev; this.self = self; this.prevCondition = prevCondition; this.selfCondition = selfCondition; } public void run() { int count = 10; while (count > 0) { prev.lock(); self.lock(); System.out.print(name); count--; selfCondition.signal(); self.unlock(); try { prevCondition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { prev.unlock(); } } self.lock(); selfCondition.signal(); self.unlock(); } public static void main(String[] args) throws Exception { ReentrantLock a = new ReentrantLock(); Condition aCondition = a.newCondition(); ReentrantLock b = new ReentrantLock(); Condition bCondition = b.newCondition(); ReentrantLock c = new ReentrantLock(); Condition cCondition = c.newCondition(); MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, cCondition, a, aCondition); MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, aCondition, b, bCondition); MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, bCondition, c, cCondition); new Thread(pa).start(); Thread.sleep(10); new Thread(pb).start(); Thread.sleep(10); new Thread(pc).start(); Thread.sleep(10); } }
2、經過一個ReentrantLock和三個conditon實現(推薦,安全性,性能和可讀性較高)
package com.demo.test; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * 基於一個ReentrantLock和三個conditon實現連續打印abcabc... * @author lixiaoxi * */ public class RcSyncPrinter implements Runnable{ // 打印次數 private static final int PRINT_COUNT = 10; // 打印鎖 private final ReentrantLock reentrantLock; // 本線程打印所需的condition private final Condition thisCondtion; // 下一個線程打印所須要的condition private final Condition nextCondtion; // 打印字符 private final char printChar; public RcSyncPrinter(ReentrantLock reentrantLock, Condition thisCondtion, Condition nextCondition, char printChar) { this.reentrantLock = reentrantLock; this.nextCondtion = nextCondition; this.thisCondtion = thisCondtion; this.printChar = printChar; } @Override public void run() { // 獲取打印鎖 進入臨界區 reentrantLock.lock(); try { // 連續打印PRINT_COUNT次 for (int i = 0; i < PRINT_COUNT; i++) { //打印字符 System.out.print(printChar); // 使用nextCondition喚醒下一個線程 // 由於只有一個線程在等待,因此signal或者signalAll均可以 nextCondtion.signal(); // 不是最後一次則經過thisCondtion等待被喚醒 // 必需要加判斷,否則雖然可以打印10次,但10次後就會直接死鎖 if (i < PRINT_COUNT - 1) { try { // 本線程讓出鎖並等待喚醒 thisCondtion.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } finally { // 釋放打印鎖 reentrantLock.unlock(); } } public static void main(String[] args) throws InterruptedException { // 寫鎖 ReentrantLock lock = new ReentrantLock(); // 打印a線程的condition Condition conditionA = lock.newCondition(); // 打印b線程的condition Condition conditionB = lock.newCondition(); // 打印c線程的condition Condition conditionC = lock.newCondition(); // 實例化A線程 Thread printerA = new Thread(new RcSyncPrinter(lock, conditionA, conditionB, 'A')); // 實例化B線程 Thread printerB = new Thread(new RcSyncPrinter(lock, conditionB, conditionC, 'B')); // 實例化C線程 Thread printerC = new Thread(new RcSyncPrinter(lock, conditionC, conditionA, 'C')); // 依次開始A B C線程 printerA.start(); Thread.sleep(100); //printerA.join(); printerB.start(); Thread.sleep(100); printerC.start(); } }
打印結果:
ABCABCABCABCABCABCABCABCABCABC
分析:
仔細想一想本問題,既然同一時刻只能有一個線程打印字符,那咱們爲何不使用一個同步鎖ReentrantLock?線程之間的喚醒操做能夠經過Condition實現,且Condition能夠有多個,每一個condition.await阻塞只能經過該condition的signal/signalall來喚醒!這是synchronized關鍵字所達不到的,那咱們就能夠給每一個打印線程一個自身的condition和下一個線程的condition,每次打印字符後,調用下一個線程的condition.signal來喚醒下一個線程,而後自身再經過本身的condition.await來釋放鎖並等待喚醒。
注意:這裏不能使用join(),由於join()方法會讓主線程等待printerA線程執行完畢,而線程中有await()方法,會一直阻塞...
下面分析下線程啓動的過程:
調用start()方法後,線程printerA啓動,這時此線程處於就緒(可運行)狀態,並無運行,一旦獲得cpu時間片,就開始執行run()方法;此時主線程繼續向下執行, Thread.sleep(100)讓主線程阻塞此時main線程讓出CPU資源,printerA得到cpu時間片,開始執行run方法,調用reentrantLock.lock();獲取鎖資源,進入同步方法,在printerA執行到thisCondtion.await()的時候,釋放鎖資源並等待被喚醒;主線程main在sleep在休眠時間事後的某個點獲取cpu資源後,繼續向下執行,此時printerB線程啓動(此時若是printerA若是尚未執行到await()方法,則在等待;若是printerA已經執行了await(),則printerB獲取鎖資源,進入同步方法),如下C線程同理..
nextCondtion.signal();喚醒是由於等待了thisCondtion.await();若是不寫nextCondtion.signal(),那麼只要等待了就沒有喚醒的機會了...
3、經過一個鎖和一個狀態變量來實現(推薦)
package com.demo.test; /** * 基於一個鎖和一個狀態變量實現連續打印abcabc... * @author lixiaoxi * */ public class StateLockPrinter { //狀態變量 private volatile int state=0; // 打印線程 private class Printer implements Runnable { //打印次數 private static final int PRINT_COUNT=10; //打印鎖 private final Object printLock; //打印標誌位 和state變量相關 private final int printFlag; //後繼線程的線程的打印標誌位,state變量相關 private final int nextPrintFlag; //該線程的打印字符 private final char printChar; public Printer(Object printLock, int printFlag,int nextPrintFlag, char printChar) { super(); this.printLock = printLock; this.printFlag=printFlag; this.nextPrintFlag=nextPrintFlag; this.printChar = printChar; } @Override public void run() { //獲取打印鎖 進入臨界區 synchronized (printLock) { //連續打印PRINT_COUNT次 for(int i=0;i<PRINT_COUNT;i++){ //循環檢驗標誌位 每次都阻塞而後等待喚醒 while (state!=printFlag) { try { printLock.wait(); } catch (InterruptedException e) { return; } } //打印字符 System.out.print(printChar); //設置狀態變量爲下一個線程的標誌位 state=nextPrintFlag; //注意要notifyall,否則會死鎖,由於notify只通知一個, //可是同時等待的是兩個,若是喚醒的不是正確那個就會沒人喚醒,死鎖了 printLock.notifyAll(); } } } } public void test() throws InterruptedException{ //鎖 Object lock=new Object(); //打印A的線程 Thread threadA=new Thread(new Printer(lock, 0,1, 'A')); //打印B的線程 Thread threadB=new Thread(new Printer(lock, 1,2, 'B')); //打印C的線程 Thread threadC=new Thread(new Printer(lock, 2,0, 'C')); //一次啓動A B C線程 threadA.start(); Thread.sleep(1000); threadB.start(); Thread.sleep(1000); threadC.start(); } public static void main(String[] args) throws InterruptedException { StateLockPrinter print = new StateLockPrinter(); print.test(); } }
打印結果:
ABCABCABCABCABCABCABCABCABCABC
分析:
狀態變量是一個volatile的整型變量,0表明打印a,1表明打印b,2表明打印c,三個線程都循環檢驗標誌位,經過阻塞前和阻塞後兩次判斷能夠確保當前打印的正確順序,隨後線程打印字符,而後設置下一個狀態字符,喚醒其它線程,而後從新進入循環。
補充題
三個Java多線程循環打印遞增的數字,每一個線程打印5個數值,打印週期1-75,一樣的解法:
package com.demo.test; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * 數字打印,三個線程同時打印數字,第一個線程打印12345,第二個線程打印678910 ......... * @author lixiaoxi * */ public class NumberPrinter { //打印計數器 private final AtomicInteger counter=new AtomicInteger(0); private class Printer implements Runnable{ //總共須要打印TOTAL_PRINT_COUNT次 private static final int TOTAL_PRINT_COUNT = 5; //每次打印PER_PRINT_COUNT次 private static final int PER_PRINT_COUNT = 5; //打印鎖 private final ReentrantLock reentrantLock; //前一個線程的condition private final Condition afterCondition; //本線程的condition private final Condition thisCondtion; public Printer(ReentrantLock reentrantLock, Condition thisCondtion,Condition afterCondition) { super(); this.reentrantLock = reentrantLock; this.afterCondition = afterCondition; this.thisCondtion = thisCondtion; } @Override public void run() { //進入臨界區 reentrantLock.lock(); try { //循環打印TOTAL_PRINT_COUNT次 for(int i=0;i<TOTAL_PRINT_COUNT;i++){ //打印操做 for(int j=0;j<PER_PRINT_COUNT;j++){ //以原子方式將當前值加 1。 //incrementAndGet返回的是新值(即加1後的值) System.out.println(counter.incrementAndGet()); } //經過afterCondition通知後面線程 afterCondition.signalAll(); if(i < TOTAL_PRINT_COUNT - 1){ try { //本線程釋放鎖並等待喚醒 thisCondtion.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } finally { reentrantLock.unlock(); } } } public void test() throws InterruptedException { //打印鎖 ReentrantLock reentrantLock=new ReentrantLock(); //打印A線程的Condition Condition conditionA=reentrantLock.newCondition(); //打印B線程的Condition Condition conditionB=reentrantLock.newCondition(); //打印C線程的Condition Condition conditionC=reentrantLock.newCondition(); //打印線程A Thread threadA=new Thread(new Printer(reentrantLock,conditionA, conditionB)); //打印線程B Thread threadB=new Thread(new Printer(reentrantLock, conditionB, conditionC)); //打印線程C Thread threadC=new Thread(new Printer(reentrantLock, conditionC, conditionA)); // 依次開啓a b c線程 threadA.start(); Thread.sleep(100); threadB.start(); Thread.sleep(100); threadC.start(); } public static void main(String[] args) throws InterruptedException { NumberPrinter print = new NumberPrinter(); print.test(); } }
運行結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75