CountDownLatch - 門閂
四個人相約出去上網,你告訴司機,等四個都上車了,您在開。所以每個人上車的時候,都會告訴司機,你來了。司機就會記錄下來,當來的人夠四個的時候,自己才能開車去往目的地。
它常用的兩個方法
countDown() // 給司機報到的方法
await() // 示意開車的方法 (就是四個都到了,才執行 此方法後邊的代碼,否則線程都阻塞)
CountDownLatch 的使用方法與上邊的思想比較類似,就是它初始化的時候會指定一個數值,當線程執行滿意之後,就可以呼喚countDown()
它一次,當呼喚到達指定次數之後,就會執行 await()
後的方法
demo
@Test
public void countDownLatchTest() throws InterruptedException {
// 定義一個司機,來四個人才能發車
CountDownLatch countDownLatch = new CountDownLatch(4);
// 定義四個線程去解決它
IntStream.range(0, 4).forEach(number -> {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + number + "到了");
// 嘗試開鎖
countDownLatch.countDown();
}, "線程" + number).start();
});
countDownLatch.await();
System.out.println("開車了");
}
輸出結果
但是,它有個不足,就只能使用一次,如果下次想再次使用,需要重新定義一個門閂,所以引出了下一個可以複用的CyclicBarrier
CyclicBarrier - 籬柵
它的思想和CountDownLatch 類似,只不過它可以重複使用
demo
@Test
public void cyclicBarrierTest() throws InterruptedException {
// 定義一個規則,出租車坐滿四個人就發車
CyclicBarrier cyclicBarrier =
new CyclicBarrier(4, () -> System.out.println("4個乘客到位,發車"));
Stream.iterate(0, n -> n+1).limit(10).forEach(n -> {
// 睡一秒,保證程序的執行順序
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "乘客已就位");
try {
// 通知一下司機,讓自己看一下,能不能發車了
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
},n.toString()).start();
});
// 睡一下,防止程序執行太快看不到結果
TimeUnit.SECONDS.sleep(10);
}
輸出結果
Exchanger - 交換機
Exchanger 的機制:就是兩個線程需要交換信息,我給了你信息,你再給我信息,咱倆才能往下走;我給了你,你不給我,我就一直阻塞着(如下圖)。
demo
@Test
public void workmateTest() throws InterruptedException {
// 定義一個交換機
Exchanger<String> workmate = new Exchanger<>();
new Thread(() -> {
try {
System.out.println();
// 用我的名字,換取你的名字
String expressDocument = workmate.exchange("01");
// 輸出換到的名字
System.out.println
(Thread.currentThread().getName()+"換到的名字:"+ expressDocument);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "thread-01").start();
new Thread(() -> {
try {
// 用我的名字,換取你的名字
String expressDocument = workmate.exchange("02");
// 輸出換到的名字
System.out.printlnSystem.out.println
(Thread.currentThread().getName()+"換到的名字:"+ expressDocument);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "thread-02").start();
// 讓主線程睡一會,不然程序直接結束了,看不到效果
Thread.sleep(5000);
}
輸出結果
上邊描述的兩個人交換的情況,但是三個人的怎麼個情況呢?
直接告訴大家結果吧:就是 隨機 兩個交換,另外一個什麼都換不到,自閉了(阻塞),多個線程也是如此。可以理解爲它只能進行兩個線程之間的數據交換。
Semaphore - 信號量
它會定義一個資源的數量,當線程申請資源的時候,數量就會減少,當數量減少爲0 的時候,其他線程再申請資源的時候,就需要等待別的線程把資源釋放的時候,等待的線程才能獲取資源。
這個過程和進停車廠停車特別類似,有空閒車位,保安會讓你進;但是沒有空閒車位,你只能等其他人出來之後,你在進去了。
小demo
@Test
public void semaphoreTest() {
// 定義5個信號量
Semaphore semaphore = new Semaphore(5);
// 用於生成隨機數
Random random = new Random();
IntStream.range(0, 15).forEach(n -> {
new Thread(() -> {
try {
// 佔用資源
semaphore.acquire();
System.out.println("佔用資源" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
// 隨機生成資源佔用的時間
int parkingTime = random.nextInt(10) + 2;
try {
TimeUnit.SECONDS.sleep(parkingTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 釋放資源
semaphore.release();
System.out.println("釋放資源" + Thread.currentThread().getName());
}, "線程" + n).start();
});
// 讓主線程睡一會,不然程序直接結束了,看不到效果
try {
Thread.sleep(50000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
輸出結果
簡做總結,如有不足,歡迎指出。