同步器
爲每種特定的同步問題提供瞭解決方案
Semaphore
Semaphore【信號標;旗語】,通過計數器控制對共享資源的訪問。
測試類:
package concurrent;
import concurrent.thread.SemaphoreThread;
import java.util.concurrent.Semaphore;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class SemaphoreTest {
public static void main(String[] args) {
//在Thread裏聲明並不是同一個對象
Semaphore semaphore = new Semaphore(3);
SemaphoreThread testA = new SemaphoreThread("A", semaphore);
SemaphoreThread testB = new SemaphoreThread("B", semaphore);
SemaphoreThread testC = new SemaphoreThread("C", semaphore);
SemaphoreThread testD = new SemaphoreThread("D", semaphore);
SemaphoreThread testE = new SemaphoreThread("E", semaphore);
SemaphoreThread testF = new SemaphoreThread("F", semaphore);
SemaphoreThread testG = new SemaphoreThread("G", semaphore);
testA.start();
testB.start();
testC.start();
testD.start();
testE.start();
testF.start();
testG.start();
}
}
線程寫法:
package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.Semaphore;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class SemaphoreThread extends Thread {
private static final Logger logger = LogManager.getLogger(SemaphoreThread.class);
//創建有3個信號量的信號量計數器
public Semaphore semaphore;
public SemaphoreThread(String name, Semaphore semaphore) {
setName(name);
this.semaphore = semaphore;
}
@Override
public void run() {
try {
logger.debug(getName() + " 取號等待... " + System.currentTimeMillis());
//取出一個信號
semaphore.acquire();
logger.debug(getName() + " 提供服務... " + System.currentTimeMillis());
sleep(1000);
logger.debug(getName() + " 完成服務... " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.debug(getName() + " 釋放... " + System.currentTimeMillis());
//釋放一個信號
semaphore.release();
}
}
執行結果【以下所有輸出結果中[]中爲線程名稱- 後爲輸出的內容】:
[C] - C 取號等待... 1470642024037
[F] - F 取號等待... 1470642024036
[E] - E 取號等待... 1470642024036
[B] - B 取號等待... 1470642024037
[D] - D 取號等待... 1470642024037
[A] - A 取號等待... 1470642023965
[D] - D 提供服務... 1470642024039
[C] - C 提供服務... 1470642024039
[G] - G 取號等待... 1470642024036
[F] - F 提供服務... 1470642024040
[D] - D 完成服務... 1470642025039
[C] - C 完成服務... 1470642025039
[D] - D 釋放... 1470642025040
[F] - F 完成服務... 1470642025040
[C] - C 釋放... 1470642025041
[B] - B 提供服務... 1470642025042
[A] - A 提供服務... 1470642025042
[F] - F 釋放... 1470642025043
[E] - E 提供服務... 1470642025043
[A] - A 完成服務... 1470642026043
[B] - B 完成服務... 1470642026043
[B] - B 釋放... 1470642026043
[A] - A 釋放... 1470642026043
[G] - G 提供服務... 1470642026044
[E] - E 完成服務... 1470642026045
[E] - E 釋放... 1470642026045
[G] - G 完成服務... 1470642027045
[G] - G 釋放... 1470642027046
可以看到,當3個信號量被領取完之後,之後的線程會阻塞在領取信號的位置,當有信號量釋放之後纔會繼續執行。
CountDownLatch
CountDownLatch【倒計時鎖】,線程中調用countDownLatch.await()使進程進入阻塞狀態,當達成指定次數後(通過countDownLatch.countDown())繼續執行每個線程中剩餘的內容。
測試類:
package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CountDownLatch;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class package concurrent;
import concurrent.thread.CountDownLatchThread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class CountDownLatchTest {
private static final Logger logger = LogManager.getLogger(CountDownLatchTest.class);
public static void main(String[] args) throws InterruptedException {
//設定當達成三個計數時觸發
CountDownLatch countDownLatch = new CountDownLatch(3);
new CountDownLatchThread("A", countDownLatch).start();
new CountDownLatchThread("B", countDownLatch).start();
new CountDownLatchThread("C", countDownLatch).start();
new CountDownLatchThread("D", countDownLatch).start();
new CountDownLatchThread("E", countDownLatch).start();
for (int i = 3; i > 0; i--) {
Thread.sleep(1000);
logger.debug(i);
countDownLatch.countDown();
}
}
}
線程類:
package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CountDownLatch;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class CountDownLatchThread extends Thread {
private static final Logger logger = LogManager.getLogger(CountDownLatchThread.class);
//計數器
private CountDownLatch countDownLatch;
public CountDownLatchThread(String name, CountDownLatch countDownLatch) {
setName(name);
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
logger.debug("執行操作...");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.debug("等待計數器達到標準...");
try {
//讓線程進入阻塞狀態,等待計數達成後釋放
countDownLatch.await();
logger.debug("計數達成,繼續執行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
執行結果:
[E] - 執行操作...
[B] - 執行操作...
[A] - 執行操作...
[C] - 執行操作...
[D] - 執行操作...
[main] DEBUG concurrent.CountDownLatchTest - 3
[B] - 等待計數器達到標準...
[E] - 等待計數器達到標準...
[C] - 等待計數器達到標準...
[D] - 等待計數器達到標準...
[A] - 等待計數器達到標準...
[main] DEBUG concurrent.CountDownLatchTest - 2
[main] DEBUG concurrent.CountDownLatchTest - 1
[E] - 計數達成,繼續執行...
[C] - 計數達成,繼續執行...
[B] - 計數達成,繼續執行...
[D] - 計數達成,繼續執行...
[A] - 計數達成,繼續執行...
CyclicBarrier
CyclicBarrier【Cyclic週期,循環的 Barrier屏障,障礙】循環的等待阻塞的線程個數到達指定數量後使參與計數的線程繼續執行並可執行特定線程(使用不同構造函數可以不設定到達後執行),其他線程仍處於阻塞等待再一次達成指定個數。
測試類:
package concurrent;
import concurrent.thread.CyclicBarrierThread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CyclicBarrier;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class CyclicBarrierTest {
private static final Logger logger = LogManager.getLogger(CyclicBarrierTest.class);
public static void main(String[] args) {
//可以使用CyclicBarrier(int parties)不設定到達後執行的內容
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
logger.debug("---計數到達後執行的內容----");
});
new CyclicBarrierThread("A", cyclicBarrier).start();
new CyclicBarrierThread("B", cyclicBarrier).start();
new CyclicBarrierThread("C", cyclicBarrier).start();
new CyclicBarrierThread("D", cyclicBarrier).start();
new CyclicBarrierThread("E", cyclicBarrier).start();
new CyclicBarrierThread("A2", cyclicBarrier).start();
new CyclicBarrierThread("B2", cyclicBarrier).start();
new CyclicBarrierThread("C2", cyclicBarrier).start();
new CyclicBarrierThread("D2", cyclicBarrier).start();
new CyclicBarrierThread("E2", cyclicBarrier).start();
//需要注意的是,如果線程數不是上面設置的等待數量的整數倍,比如這個程序中又加了個線程,
// 那麼當達到5個數量時,只會執行達到時的五個線程的內容,
// 剩餘一個線程會出於阻塞狀態導致主線程無法退出,程序無法結束
// new CyclicBarrierThread("F", cyclicBarrier).start();//將這行註釋去掉程序無法自動結束
}
}
線程類:
package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class CyclicBarrierThread extends Thread {
private static final Logger logger = LogManager.getLogger(CyclicBarrierThread.class);
private CyclicBarrier cyclicBarrier;
public CyclicBarrierThread(String name, CyclicBarrier cyclicBarrier) {
super(name);
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
logger.debug("執行操作...");
try {
int time = new Random().nextInt(10) * 1000;
logger.debug("休眠" + time/1000 + "秒");
sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.debug("等待計數器達到標準...");
try {
//讓線程進入阻塞狀態,等待計數達成後釋放
cyclicBarrier.await();
logger.debug("計數達成,繼續執行...");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
執行結果:
[A] - 執行操作...
[A] - 休眠0秒
[E2] - 執行操作...
[E2] - 休眠5秒
[D2] - 執行操作...
[D2] - 休眠4秒
[C2] - 執行操作...
[C2] - 休眠4秒
[B2] - 執行操作...
[B2] - 休眠6秒
[A2] - 執行操作...
[A2] - 休眠8秒
[E] - 執行操作...
[E] - 休眠5秒
[D] - 執行操作...
[D] - 休眠0秒
[C] - 執行操作...
[C] - 休眠3秒
[B] - 執行操作...
[B] - 休眠7秒
[A] - 等待計數器達到標準...
[D] - 等待計數器達到標準...
[C] - 等待計數器達到標準...
[D2] - 等待計數器達到標準...
[C2] - 等待計數器達到標準...
[C2] DEBUG concurrent.CyclicBarrierTest - ---計數到達後執行的內容----
[C2] - 計數達成,繼續執行...
[A] - 計數達成,繼續執行...
[C] - 計數達成,繼續執行...
[D2] - 計數達成,繼續執行...
[D] - 計數達成,繼續執行...
[E2] - 等待計數器達到標準...
[E] - 等待計數器達到標準...
[B2] - 等待計數器達到標準...
[B] - 等待計數器達到標準...
[A2] - 等待計數器達到標準...
[A2] DEBUG concurrent.CyclicBarrierTest - ---計數到達後執行的內容----
[E] - 計數達成,繼續執行...
[B2] - 計數達成,繼續執行...
[E2] - 計數達成,繼續執行...
[B] - 計數達成,繼續執行...
[A2] - 計數達成,繼續執行...
可以想象成以前不正規的長途汽車站的模式:
不正規的長途汽車站會等待座位坐滿之後才發車,到達目的地之後繼續等待然後循環進行。每個人都是一個Thread,上車後觸發cyclicBarrier.await();,當坐滿時就是達到指定達成數的時候,車輛發車就是達成後統一執行的內容,發車後車上的人們就可以聊天之類的操作了【我們暫且理解爲上車後人們就都不能動了O(∩_∩)O~】。
CountDownLatch與CyclicBarrier區別:
CountDownLatch是一個或多個線程等待計數達成後繼續執行,await()調用並沒有參與計數。
CyclicBarrier則是N個線程等待彼此執行到零界點之後再繼續執行,await()調用的同時參與了計數,並且CyclicBarrier支持條件達成後執行某個動作,而且這個過程是循環性的。
Exchanger
Exchanger 用於線程間進行數據交換
測試類:
package concurrent;
import concurrent.pojo.ExchangerPojo;
import concurrent.thread.ExchangerThread;
import java.util.HashMap;
import java.util.concurrent.Exchanger;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class ExchangerTest {
public static void main(String[] args) {
Exchanger<HashMap<String, ExchangerPojo>> exchanger = new Exchanger<>();
new ExchangerThread("A", exchanger).start();
new ExchangerThread("B", exchanger).start();
}
}
實體類:
package concurrent.pojo;
import com.alibaba.fastjson.JSON;
import java.util.Date;
import java.util.List;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class ExchangerPojo {
private int intVal;
private String strVal;
private List<String> strList;
private Date date;
public ExchangerPojo(int intVal, String strVal, List<String> strList, Date date) {
this.intVal = intVal;
this.strVal = strVal;
this.strList = strList;
this.date = date;
}
public int getIntVal() {
return intVal;
}
public void setIntVal(int intVal) {
this.intVal = intVal;
}
public String getStrVal() {
return strVal;
}
public void setStrVal(String strVal) {
this.strVal = strVal;
}
public List<String> getStrList() {
return strList;
}
public void setStrList(List<String> strList) {
this.strList = strList;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
線程類:
package concurrent.thread;
import concurrent.pojo.ExchangerPojo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.concurrent.Exchanger;
/**
* 拿客
* www.coderknock.com
* QQ羣:213732117
* 創建時間:2016年08月08日
* 描述:
*/
public class ExchangerThread extends Thread {
private Exchanger<HashMap<String, ExchangerPojo>> exchanger;
private static final Logger logger = LogManager.getLogger(ExchangerThread.class);
public ExchangerThread(String name, Exchanger<HashMap<String, ExchangerPojo>> exchanger) {
super(name);
this.exchanger = exchanger;
}
@Override
public void run() {
HashMap<String, ExchangerPojo> map = new HashMap<>();
logger.debug(getName() + "提供者提供數據...");
Random random = new Random();
for (int i = 0; i < 3; i++) {
int index = random.nextInt(10);
List<String> list = new ArrayList<>();
for (int j = 0; j < index; j++) {
list.add("list ---> " + j);
}
ExchangerPojo pojo = new ExchangerPojo(index, getName() + "提供的數據", list, new Date());
map.put("第" + i + "個數據", pojo);
}
try {
int time = random.nextInt(10);
logger.debug(getName() + "等待" + time + "秒....");
for (int i = time; i > 0; i--) {
sleep(1000);
logger.debug(getName() + "---->" + i);
}
//等待exchange是會進入阻塞狀態,可以在一個線程中與另一線程多次交互,此處就不寫多次了
HashMap<String, ExchangerPojo> getMap = exchanger.exchange(map);
time = random.nextInt(10);
logger.debug(getName() + "接受到數據等待" + time + "秒....");
for (int i = time; i > 0; i--) {
sleep(1000);
logger.debug(getName() + "---->" + i);
}
getMap.forEach((x, y) -> {
logger.debug(x + " -----> " + y.toString());
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
執行結果:
[B] - B提供者提供數據...
[A] - A提供者提供數據...
[A] - A等待2秒....
[B] - B等待0秒....
[A] - A---->2
[A] - A---->1
[B] - B接受到數據等待1秒....
[A] - A接受到數據等待4秒....
[B] - B---->1
[A] - A---->4
[B] - 第0個數據 -----> {"date":1470652252049,"intVal":5,"strList":["list ---> 0","list ---> 1","list ---> 2","list ---> 3","list ---> 4"],"strVal":"A提供的數據"}
[B] - 第1個數據 -----> {"date":1470652252049,"intVal":1,"strList":["list ---> 0"],"strVal":"A提供的數據"}
[B] - 第2個數據 -----> {"date":1470652252049,"intVal":4,"strList":["list ---> 0","list ---> 1","list ---> 2","list ---> 3"],"strVal":"A提供的數據"}
[A] - A---->3
[A] - A---->2
[A] - A---->1
[A] - 第0個數據 -----> {"date":1470652252057,"intVal":1,"strList":["list ---> 0"],"strVal":"B提供的數據"}
[A] - 第1個數據 -----> {"date":1470652252057,"intVal":6,"strList":["list ---> 0","list ---> 1","list ---> 2","list ---> 3","list ---> 4","list ---> 5"],"strVal":"B提供的數據"}
[A] - 第2個數據 -----> {"date":1470652252057,"intVal":6,"strList":["list ---> 0","list ---> 1","list ---> 2","list ---> 3","list ---> 4","list ---> 5"],"strVal":"B提供的數據"}
Phaser
Phaser個人感覺兼具了CountDownLatch與CyclicBarrier的功能,並提供了分階段的能力。
實現分階段的CyclicBarrier的功能
測試代碼:
package concurrent;
import concurrent.thread.PhaserThread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.Phaser;
/**
* 拿客
* 網站:www.coderknock.com
* QQ羣:213732117
* 三產 創建於 2016年08月08日 21:25:30。
*/
public class PhaserTest {
private static final Logger logger = LogManager.getLogger(PhaserTest.class);
public static void main(String[] args) {
Phaser phaser = new Phaser() {
/**此方法有2個作用:
* 1、當每一個階段執行完畢,此方法會被自動調用,因此,重載此方法寫入的代碼會在每個階段執行完畢時執行,相當於CyclicBarrier的barrierAction。
* 2、當此方法返回true時,意味着Phaser被終止,因此可以巧妙的設置此方法的返回值來終止所有線程。例如:若此方法返回值爲 phase>=3,其含義爲當整個線程執行了4個階段後,程序終止。
* */
@Override
protected boolean onAdvance(int phase, int registeredParties) {
logger.debug("階段--->" + phase);
logger.debug("註冊的線程數量--->" + registeredParties);
return super.onAdvance(phase, registeredParties);
}
};
for (int i = 3; i > 0; i--) {
new PhaserThread("第" + i + "個", phaser).start();
}
}
}
線程代碼:
package concurrent.thread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Random;
import java.util.concurrent.Phaser;
/**
* 拿客
* 網站:www.coderknock.com
* QQ羣:213732117
* 三產 創建於 2016年08月08日 21:16:55。
*/
public class PhaserThread extends Thread {
private Phaser phaser;
private static final Logger logger = LogManager.getLogger(PhaserThread.class);
public PhaserThread(String name, Phaser phaser) {
super(name);
this.phaser = phaser;
//把當前線程註冊到Phaser
this.phaser.register();
logger.debug("name爲" + name + "的線程註冊了" + this.phaser.getRegisteredParties() + "個線程");
}
@Override
public void run() {
logger.debug("進入...");
phaser.arrive();
for (int i = 6; i > 0; i--) {
int time = new Random().nextInt(5);
try {
logger.debug("睡眠" + time + "秒");
sleep(time * 1000);
if (i == 1) {
logger.debug("未完成的線程數量:" + phaser.getUnarrivedParties());
logger.debug("最後一次觸發,並註銷自身");
phaser.arriveAndDeregister();
logger.debug("未完成的線程數量:" + phaser.getUnarrivedParties());
} else {
logger.debug("未完成的線程數量:" + phaser.getUnarrivedParties());
logger.debug(i + "--->觸發並阻塞...");
phaser.arriveAndAwaitAdvance();//相當於CyclicBarrier.await();
logger.debug("未完成的線程數量:" + phaser.getUnarrivedParties());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
logger.debug("註銷完成之後註冊的線程數量--->" + phaser.getRegisteredParties());
}
}
執行結果:
[main] - name爲第3個的線程註冊了1個線程
[main] - name爲第2個的線程註冊了2個線程
[main] - name爲第1個的線程註冊了3個線程
[第3個] - 進入...
[第2個] - 進入...
[第3個] - 睡眠2秒
[第2個] - 睡眠1秒
[第1個] - 進入...
[第1個] - 階段--->0
[第1個] - 註冊的線程數量--->3
[第1個] - 睡眠4秒
[第2個] - 未完成的線程數量:3
[第2個] - 6--->觸發並阻塞...
[第3個] - 未完成的線程數量:2
[第3個] - 6--->觸發並阻塞...
[第1個] - 未完成的線程數量:1
[第1個] - 6--->觸發並阻塞...
[第1個] - 階段--->1
[第1個] - 註冊的線程數量--->3
[第1個] - 未完成的線程數量:3
[第3個] - 未完成的線程數量:3
[第2個] - 未完成的線程數量:3
[第1個] - 睡眠1秒
[第3個] - 睡眠0秒
[第2個] - 睡眠4秒
[第3個] - 未完成的線程數量:3
[第3個] - 5--->觸發並阻塞...
[第1個] - 未完成的線程數量:2
[第1個] - 5--->觸發並阻塞...
[第2個] - 未完成的線程數量:1
[第2個] - 5--->觸發並阻塞...
[第2個] - 階段--->2
[第2個] - 註冊的線程數量--->3
[第2個] - 未完成的線程數量:3
[第3個] - 未完成的線程數量:3
[第1個] - 未完成的線程數量:3
[第2個] - 睡眠0秒
[第3個] - 睡眠2秒
[第2個] - 未完成的線程數量:3
[第1個] - 睡眠2秒
[第2個] - 4--->觸發並阻塞...
[第3個] - 未完成的線程數量:2
[第1個] - 未完成的線程數量:2
[第3個] - 4--->觸發並阻塞...
[第1個] - 4--->觸發並阻塞...
[第1個] - 階段--->3
[第1個] - 註冊的線程數量--->3
[第1個] - 未完成的線程數量:3
[第3個] - 未完成的線程數量:3
[第2個] - 未完成的線程數量:3
[第1個] - 睡眠2秒
[第3個] - 睡眠1秒
[第2個] - 睡眠4秒
[第3個] - 未完成的線程數量:3
[第3個] - 3--->觸發並阻塞...
[第1個] - 未完成的線程數量:2
[第1個] - 3--->觸發並阻塞...
[第2個] - 未完成的線程數量:1
[第2個] - 3--->觸發並阻塞...
[第2個] - 階段--->4
[第2個] - 註冊的線程數量--->3
[第2個] - 未完成的線程數量:3
[第3個] - 未完成的線程數量:3
[第1個] - 未完成的線程數量:3
[第2個] - 睡眠2秒
[第1個] - 睡眠2秒
[第3個] - 睡眠4秒
[第2個] - 未完成的線程數量:3
[第1個] - 未完成的線程數量:3
[第2個] - 2--->觸發並阻塞...
[第1個] - 2--->觸發並阻塞...
[第3個] - 未完成的線程數量:1
[第3個] - 2--->觸發並阻塞...
[第3個] - 階段--->5
[第3個] - 註冊的線程數量--->3
[第3個] - 未完成的線程數量:3
[第1個] - 未完成的線程數量:3
[第2個] - 未完成的線程數量:3
[第3個] - 睡眠2秒
[第1個] - 睡眠3秒
[第2個] - 睡眠0秒
[第2個] - 未完成的線程數量:3
[第2個] - 最後一次觸發,並註銷自身
[第2個] - 未完成的線程數量:2
[第2個] - 註銷完成之後註冊的線程數量--->2
[第3個] - 未完成的線程數量:2
[第3個] - 最後一次觸發,並註銷自身
[第3個] - 未完成的線程數量:1
[第3個] - 註銷完成之後註冊的線程數量--->1
[第1個] - 未完成的線程數量:1
[第1個] - 最後一次觸發,並註銷自身
[第1個] - 階段--->6
[第1個] - 註冊的線程數量--->0
[第1個] - 未完成的線程數量:0
[第1個] - 註銷完成之後註冊的線程數量--->0
上面代碼中,當所有線程進行到arriveAndAwaitAdvance()時會觸發計數並且將線程阻塞,等計數數量等於註冊線程數量【即所有線程都執行到了約定的地方時,會放行,是所有線程得以繼續執行,並觸發onAction事件】。我們可以在onAction中根據不同階段執行不同內容的操作。
實現分階段的CountDownLatch的功能
只需將上面的測試類更改如下:
package concurrent;
import concurrent.thread.PhaserThread;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.Phaser;
import static jodd.util.ThreadUtil.sleep;
/**
* 拿客
* 網站:www.coderknock.com
* QQ羣:213732117
* 三產 創建於 2016年08月08日 21:25:30。
*/
public class PhaserTest {
private static final Logger logger = LogManager.getLogger(PhaserTest.class);
public static void main(String[] args) {
//這裏其實相當於已經註冊了3個線程,但是並沒有實際的線程
int coutNum=3;
Phaser phaser = new Phaser(coutNum) {
/**此方法有2個作用:
* 1、當每一個階段執行完畢,此方法會被自動調用,因此,重載此方法寫入的代碼會在每個階段執行完畢時執行,相當於CyclicBarrier的barrierAction。
* 2、當此方法返回true時,意味着Phaser被終止,因此可以巧妙的設置此方法的返回值來終止所有線程。例如:若此方法返回值爲 phase>=3,其含義爲當整個線程執行了4個階段後,程序終止。
* */
@Override
protected boolean onAdvance(int phase, int registeredParties) {
logger.debug("階段--->" + phase);
logger.debug("註冊的線程數量--->" + registeredParties);
return registeredParties==coutNum;//當後只剩下coutNum個線程時說明所有真實的註冊的線程已經運行完成,測試可以終止Phaser
}
};
for (int i = 3; i > 0; i--) {
new PhaserThread("第" + i + "個", phaser).start();
}
//當phaser未終止時循環註冊這塊兒可以使用實際的業務處理
while (!phaser.isTerminated()) {
sleep(1000);
logger.debug("觸發一次");
phaser.arrive(); //相當於countDownLatch.countDown();
}
}
}