讓線程按順序執(zhí)行8種方法
一.前言
本文使用了8種方法實現(xiàn)在多線程中讓線程按順序運行的方法,涉及到多線程中許多常用的方法,不止為了知道如何讓線程按順序運行,更是讓讀者對多線程的使用有更深刻的了解。使用的方法如下:
[1] 使用線程的join方法[
2] 使用主線程的join方法
[3] 使用線程的wait方法
[4] 使用線程的線程池方法
[5] 使用線程的Condition(條件變量)方法
[6] 使用線程的CountDownLatch(倒計數(shù))方法
[7] 使用線程的CyclicBarrier(回環(huán)柵欄)方法
[8] 使用線程的Semaphore(信號量)方法
二.實現(xiàn)
我們下面需要完成這樣一個應用場景:
1.早上;2.測試人員、產(chǎn)品經(jīng)理、開發(fā)人員陸續(xù)的來公司上班;3.產(chǎn)品經(jīng)理規(guī)劃新需求;4.開發(fā)人員開發(fā)新需求功能;5.測試人員測試新功能。
規(guī)劃需求,開發(fā)需求新功能,測試新功能是一個有順序的,我們把thread1看做產(chǎn)品經(jīng)理,thread2看做開發(fā)人員,thread3看做測試人員。
1.使用線程的join方法
join():是Theard的方法,作用是調(diào)用線程需等待該join()線程執(zhí)行完成后,才能繼續(xù)用下運行。
應用場景:當一個線程必須等待另一個線程執(zhí)行完畢才能執(zhí)行時可以使用join方法。

運行結(jié)果
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 開發(fā)人員和測試人員休息會…
- 產(chǎn)品經(jīng)理正在規(guī)劃新需求…
- 產(chǎn)品經(jīng)理新需求規(guī)劃完成!
- 測試人員休息會…
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
2.使用主線程的join方法
這里是在主線程中使用join()來實現(xiàn)對線程的阻塞。

運行結(jié)果
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 開發(fā)人員和測試人員休息會…
- 產(chǎn)品經(jīng)理正在規(guī)劃新需求…
- 產(chǎn)品經(jīng)理新需求規(guī)劃完成!
- 測試人員休息會…
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
3.使用線程的wait方法
wait():是Object的方法,作用是讓當前線程進入等待狀態(tài),同時,wait()也會讓當前線程釋放它所持有的鎖。“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法”,當前線程被喚醒(進入“就緒狀態(tài)”)
notify()和notifyAll():是Object的方法,作用則是喚醒當前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程。
wait(long timeout):讓當前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的notify()方法或 notifyAll() 方法,或者超過指定的時間量”,當前線程被喚醒(進入“就緒狀態(tài)”)。
應用場景:Java實現(xiàn)生產(chǎn)者消費者的方式。


運行結(jié)果:這里輸出會有很多種順序,主要是因為線程進入的順序,造成鎖住線程的順序不一致。
- 早上:
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 領(lǐng)導吩咐:
- 首先,產(chǎn)品經(jīng)理規(guī)劃新需求…
- 然后,開發(fā)人員開發(fā)新需求功能…
- 最后,測試人員測試新功能…
- 產(chǎn)品經(jīng)理規(guī)劃新需求
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
4.使用線程的線程池方法
JAVA通過Executors提供了四種線程池
- 單線程化線程池(newSingleThreadExecutor);
- 可控最大并發(fā)數(shù)線程池(newFixedThreadPool);
- 可回收緩存線程池(newCachedThreadPool);
- 支持定時與周期性任務的線程池(newScheduledThreadPool)。
單線程化線程池(newSingleThreadExecutor):優(yōu)點,串行執(zhí)行所有任務。
submit():提交任務。
shutdown():方法用來關(guān)閉線程池,拒絕新任務。
應用場景:串行執(zhí)行所有任務。如果這個唯一的線程因為異常結(jié)束,那么會有一個新的線程來替代它。此線程池保證所有任務的執(zhí)行順序按照任務的提交順序執(zhí)行。

運行結(jié)果
- 早上:
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 領(lǐng)導吩咐:
- 首先,產(chǎn)品經(jīng)理規(guī)劃新需求…
- 然后,開發(fā)人員開發(fā)新需求功能…
- 最后,測試人員測試新功能…
- 產(chǎn)品經(jīng)理規(guī)劃新需求
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
5.使用線程的Condition(條件變量)方法
Condition(條件變量):通常與一個鎖關(guān)聯(lián)。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構(gòu)造方法,否則它將自己生成一個RLock實例。
- Condition中await()方法類似于Object類中的wait()方法。
- Condition中await(long time,TimeUnit unit)方法類似于Object類中的wait(long time)方法。
- Condition中signal()方法類似于Object類中的notify()方法。
- Condition中signalAll()方法類似于Object類中的notifyAll()方法。
應用場景:Condition是一個多線程間協(xié)調(diào)通信的工具類,使得某個,或者某些線程一起等待某個條件(Condition),只有當該條件具備( signal 或者 signalAll方法被調(diào)用)時 ,這些等待線程才會被喚醒,從而重新爭奪鎖。


運行結(jié)果:這里輸出會有很多種順序,主要是因為線程進入的順序,造成鎖住線程的順序不一致
- 早上:
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 領(lǐng)導吩咐:
- 首先,產(chǎn)品經(jīng)理規(guī)劃新需求…
- 然后,開發(fā)人員開發(fā)新需求功能…
- 最后,測試人員測試新功能…
- 產(chǎn)品經(jīng)理規(guī)劃新需求
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
6.使用線程的CountDownLatch(倒計數(shù))方法
CountDownLatch:位于java.util.concurrent包下,利用它可以實現(xiàn)類似計數(shù)器的功能。
應用場景:比如有一個任務C,它要等待其他任務A,B執(zhí)行完畢之后才能執(zhí)行,此時就可以利用CountDownLatch來實現(xiàn)這種功能了。


運行結(jié)果
- 早上:
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 領(lǐng)導吩咐:
- 首先,產(chǎn)品經(jīng)理規(guī)劃新需求…
- 然后,開發(fā)人員開發(fā)新需求功能…
- 最后,測試人員測試新功能…
- 產(chǎn)品經(jīng)理規(guī)劃新需求
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
7.使用CyclicBarrier(回環(huán)柵欄)實現(xiàn)線程按順序運行
CyclicBarrier(回環(huán)柵欄):通過它可以實現(xiàn)讓一組線程等待至某個狀態(tài)之后再全部同時執(zhí)行。叫做回環(huán)是因為當所有等待線程都被釋放以后,CyclicBarrier可以被重用。我們暫且把這個狀態(tài)就叫做barrier,當調(diào)用await()方法之后,線程就處于barrier了。
應用場景:公司組織春游,等待所有的員工到達集合地點才能出發(fā),每個人到達后進入barrier狀態(tài)。都到達后,喚起大家一起出發(fā)去旅行。


運行結(jié)果
- 早上:
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 領(lǐng)導吩咐:
- 首先,產(chǎn)品經(jīng)理規(guī)劃新需求…
- 然后,開發(fā)人員開發(fā)新需求功能…
- 最后,測試人員測試新功能…
- 產(chǎn)品經(jīng)理規(guī)劃新需求
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
8.使用Sephmore(信號量)實現(xiàn)線程按順序運行
Sephmore(信號量):Semaphore是一個計數(shù)信號量,從概念上將,Semaphore包含一組許可證,如果有需要的話,每個acquire()方法都會阻塞,直到獲取一個可用的許可證,每個release()方法都會釋放持有許可證的線程,并且歸還Semaphore一個可用的許可證。然而,實際上并沒有真實的許可證對象供線程使用,Semaphore只是對可用的數(shù)量進行管理維護。
acquire():當前線程嘗試去阻塞的獲取1個許可證,此過程是阻塞的,當前線程獲取了1個可用的許可證,則會停止等待,繼續(xù)執(zhí)行。
release():當前線程釋放1個可用的許可證。
應用場景:Semaphore可以用來做流量分流,特別是對公共資源有限的場景,比如數(shù)據(jù)庫連接。假設有這個的需求,讀取幾萬個文件的數(shù)據(jù)到數(shù)據(jù)庫中,由于文件讀取是IO密集型任務,可以啟動幾十個線程并發(fā)讀取,但是數(shù)據(jù)庫連接數(shù)只有10個,這時就必須控制最多只有10個線程能夠拿到數(shù)據(jù)庫連接進行操作。這個時候,就可以使用Semaphore做流量控制。


運行結(jié)果
- 早上:
- 產(chǎn)品經(jīng)理來上班了
- 測試人員來上班了
- 開發(fā)人員來上班了
- 領(lǐng)導吩咐:
- 首先,產(chǎn)品經(jīng)理規(guī)劃新需求…
- 然后,開發(fā)人員開發(fā)新需求功能…
- 最后,測試人員測試新功能…
- 產(chǎn)品經(jīng)理規(guī)劃新需求
- 開發(fā)人員開發(fā)新需求功能
- 測試人員測試新功能
總結(jié)
看完了這么多種方法,是不是對多線程有了更深入的了解呢?不妨自己試試吧(代碼拷貝均可運行)
使用的場景還有很多,根據(jù)開發(fā)需求場景,選擇合適的方法,達到事半功倍的效果。