1、java.util.concurrent.CountDownLatch;
CountDownLatch 又叫閉鎖,可以讓一個線程等待其他一組線程都執行結束之後再繼續執行,如果在主方法中使用,就會將主線程阻塞,等待指定個數的線程都執行結束之後,主線程在恢復執行。
舉個例子:就相當於在比賽的時候,一個裁判,要等待所有運動員都到了終點之後,裁判纔會結束比賽
CountDownLatch中有幾個常用的方法,如下:
- public CountDownLatch( int count ) : 其中count表示要等待的線程個數
- public void await( ) : 在需要阻塞的線程中調用這個方法,表示將當前線程阻塞
- public void countDown( ) :執行一次這個方法將要等待的線程個數減一,當計數器減爲0表示所有線程執行完了
CountDownLatch的使用:
/**
* 運動員線程和裁判線程
* 裁判要等最後一個運動員到達終點之後再結束比賽
*/
class CountDownLatchTest implements Runnable{
private CountDownLatch downLatch ;
public CountDownLatchTest(CountDownLatch downLatch) {
this.downLatch = downLatch;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到達終點");
downLatch.countDown();
}
}
/**
* 主線程相當於裁判線程,等待運動員線程都執行完了之後再宣佈比賽結束
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5); // 5表示要等待的線程數
CountDownLatchTest test = new CountDownLatchTest(countDownLatch);
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(test, "運行員"+i);
list.add(thread) ;
}
System.out.println("-----比賽開始-----");
for (Thread t : list){
t.start();
}
countDownLatch.await();
System.out.println("-----比賽結束-----");
}
}
運行結果:
如果我們將countDownLatch.await( )方法註釋掉,運行結果應該是(這個運行結果不固定,也有可能是結束比賽再運動員線程中間打印):主線程和其他線程各運行各的
從上面兩個圖應該很容易看出來區別。
注意:CountDownLatch的值減爲0時,不可恢復,所以有叫閉鎖
2、java.util.concurrent.CyclicBarrier 循環柵欄
CyclicBarrier 有叫循環柵欄,它的作用是等待一組線程都執行到某個狀態後再繼續執行;看起來好像和CountDownLatch差不多,下面來看一個例子具體說明CyclicBarrier的作用:
模擬一個開會的場景:
現在有幾個人要開會,需要等待所有人都到齊了之後再開會,假如現在第一個人到了,第一個人調用await方法,然後將第一個人阻塞着,等待其他的人,後面再有來的人都調用await方法阻塞着等後面的人,直到所有人都到了,再一起開始開會 ;
CyclicBarrier中的核心方法:
- public int await( ) throws InterruptedException, BrokenBarrierException
- 阻塞當前線程,讓當前線程等待其他線程
- public CyclicBarrier(int parties)
- 構造方法 其中parties表示等待的線程個數
- public CyclicBarrier(int parties, Runnable barrierAction)
- parties 表示等待的線程個數
- barrierAction 傳入一個Runnable對象,表示當所有線程到達柵欄的位置之後再所有線程中隨便挑選一個線程去執行這個Runnable對象中的任務,執行完這個任務之後再讓所有線程同時開始往下執行
class CyclicBarrierTest implements Runnable{
private CyclicBarrier cyclicBarrier ;
public CyclicBarrierTest(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到達會議室");
try {
cyclicBarrier.await(); // 柵欄,先要等所有線程都到這個位置
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"總結上週工作");
}
}
public class Test {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
// 傳入一個Runnable對象,等所有線程到達柵欄位置之後再隨便挑選一個線程執行這個任務 打印會議開始
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable(){
@Override
public void run(){
System.out.println("會議開始");
}
});
CyclicBarrierTest test = new CyclicBarrierTest(cyclicBarrier);
List<Thread> list = new ArrayList<>();
for (int i=0; i<5; i++)
{
Thread thread = new Thread(test, "成員"+i);
list.add(thread);
}
System.out.println("等待所有人到達會場...");
for (Thread t : list)
{
t.start();
}
}
}
運行結果:
3、Exchanger線程交換器
Exchange主要用於再兩個線程之間交換數據,就是當兩個線程配對之後(調用Exchanger裏面的exchange方法),將兩個線程間的數據交換,然後再一起執行,如果只有一個線程調用exchange方法,這個線程將阻塞,直到有另一個線程和他配對,下面看例子:
// 一個Boy線程
class Boy implements Runnable{
private Exchanger exchanger ;
public Boy(Exchanger exchanger) {
this.exchanger = exchanger;
}
@Override
public void run() {
String say = "我是24K純爺們..." ;
try {
say = (String) exchanger.exchange(say);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("男孩說:"+say);
}
}
// 一個Girl線程
class Gril implements Runnable{
private Exchanger exchanger ;
public Gril(Exchanger exchanger) {
this.exchanger = exchanger;
}
@Override
public void run() {
String say = "我是可愛的小仙女...";
try {
say = (String) exchanger.exchange(say);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("女孩說:"+say);
}
}
public class Test {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>() ;
Boy boy = new Boy(exchanger) ;
Gril gril = new Gril(exchanger) ;
new Thread(boy).start() ;
new Thread(gril).start() ;
}
}
運行結果: