前言 之前學多線程的時候沒有學習線程的同步工具類(輔助類)。ps:當時覺得暫時用不上,認為是挺高深的知識點就沒去管了.. 在前幾天,朋友發了一篇比較好的Semaphore文章過來,然後在瀏覽博客的時候又發現面試還會考,那還是挺重要的知識點。於是花了點時間去瞭解一下。 Java為我們提供了 三個同步工 ...
前言
之前學多線程的時候沒有學習線程的同步工具類(輔助類)。ps:當時覺得暫時用不上,認為是挺高深的知識點就沒去管了..
在前幾天,朋友發了一篇比較好的Semaphore文章過來,然後在瀏覽博客的時候又發現面試還會考,那還是挺重要的知識點。於是花了點時間去瞭解一下。
Java為我們提供了三個同步工具類:
- CountDownLatch(閉鎖)
- CyclicBarrier(柵欄)
- Semaphore(信號量)
這幾個工具類其實說白了就是為了能夠更好控制線程之間的通訊問題~
一、CountDownLatch
1.1CountDownLatch簡介
- A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
簡單來說:CountDownLatch是一個同步的輔助類,允許一個或多個線程一直等待,直到其它線程完成它們的操作。
它常用的API其實就兩個:await()
和countDown()
使用說明:
- count初始化CountDownLatch,然後需要等待的線程調用await方法。await方法會一直受阻塞直到count=0。而其它線程完成自己的操作後,調用
countDown()
使計數器count減1。當count減到0時,所有在等待的線程均會被釋放 - 說白了就是通過count變數來控制等待,如果count值為0了(其他線程的任務都完成了),那就可以繼續執行。
1.2CountDownLatch例子
例子:3y現在去做實習生了,其他的員工還沒下班,3y不好意思先走,等其他的員工都走光了,3y再走。
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(5);
System.out.println("現在6點下班了.....");
// 3y線程啟動
new Thread(new Runnable() {
@Override
public void run() {
try {
// 這裡調用的是await()不是wait()
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("...其他的5個員工走光了,3y終於可以走了");
}
}).start();
// 其他員工線程啟動
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("員工xxxx下班了");
countDownLatch.countDown();
}
}).start();
}
}
}
輸出結果:
再寫個例子:3y現在負責倉庫模塊功能,但是能力太差了,寫得很慢,別的員工都需要等3y寫好了才能繼續往下寫。
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
// 3y線程啟動
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("3y終於寫完了");
countDownLatch.countDown();
}
}).start();
// 其他員工線程啟動
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("其他員工需要等待3y");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("3y終於寫完了,其他員工可以開始了!");
}
}).start();
}
}
}
輸出結果:
參考資料:
- https://blog.csdn.net/qq_19431333/article/details/68940987
- https://blog.csdn.net/panweiwei1994/article/details/78826072
- http://www.importnew.com/15731.html
二、CyclicBarrier
2.1CyclicBarrier簡介
- A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.
簡單來說:CyclicBarrier允許一組線程互相等待,直到到達某個公共屏障點。叫做cyclic是因為當所有等待線程都被釋放以後,CyclicBarrier可以被重用(對比於CountDownLatch是不能重用的)
使用說明:
- CountDownLatch註重的是等待其他線程完成,CyclicBarrier註重的是:當線程到達某個狀態後,暫停下來等待其他線程,所有線程均到達以後,繼續執行。
2.2CyclicBarrier例子
例子:3y和女朋友約了去廣州夜上海吃東西,由於3y和3y女朋友住的地方不同,自然去的路徑也就不一樣了。於是他倆約定在體育西路地鐵站集合,約定等到相互見面的時候就發一條朋友圈。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test {
public static void main(String[] args) {
final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);
for (int i = 0; i < 2; i++) {
new Thread(() -> {
String name = Thread.currentThread().getName();
if (name.equals("Thread-0")) {
name = "3y";
} else {
name = "女朋友";
}
System.out.println(name + "到了體育西");
try {
// 兩個人都要到體育西才能發朋友圈
CyclicBarrier.await();
// 他倆到達了體育西,看見了對方發了一條朋友圈:
System.out.println("跟" + name + "去夜上海吃東西~");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
測試結果:
玩了一天以後,各自回到家裡,3y和女朋友約定各自洗澡完之後再聊天
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test {
public static void main(String[] args) {
final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);
for (int i = 0; i < 2; i++) {
new Thread(() -> {
String name = Thread.currentThread().getName();
if (name.equals("Thread-0")) {
name = "3y";
} else {
name = "女朋友";
}
System.out.println(name + "到了體育西");
try {
// 兩個人都要到體育西才能發朋友圈
CyclicBarrier.await();
// 他倆到達了體育西,看見了對方發了一條朋友圈:
System.out.println("跟" + name + "去夜上海吃東西~");
// 回家
CyclicBarrier.await();
System.out.println(name + "洗澡");
// 洗澡完之後一起聊天
CyclicBarrier.await();
System.out.println("一起聊天");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
測試結果:
參考資料:
三、Semaphore
3.1Semaphore簡介
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
- A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each {@link #acquire} blocks if necessary until a permit is available, and then takes it. Each {@link #release} adds a permit,potentially releasing a blocking acquirer.However, no actual permit objects are used; the {@code Semaphore} just
keeps a count of the number available and acts accordingly.
Semaphore(信號量)實際上就是可以控制同時訪問的線程個數,它維護了一組"許可證"。
- 當調用
acquire()
方法時,會消費一個許可證。如果沒有許可證了,會阻塞起來 - 當調用
release()
方法時,會添加一個許可證。 - 這些"許可證"的個數其實就是一個count變數罷了~
3.2Semaphore例子
3y女朋友開了一間賣酸奶的小店,小店一次只能容納5個顧客挑選購買,超過5個就需要排隊啦~~~
import java.util.concurrent.Semaphore;
public class Test {
public static void main(String[] args) {
// 假設有50個同時來到酸奶店門口
int nums = 50;
// 酸奶店只能容納10個人同時挑選酸奶
Semaphore semaphore = new Semaphore(10);
for (int i = 0; i < nums; i++) {
int finalI = i;
new Thread(() -> {
try {
// 有"號"的才能進酸奶店挑選購買
semaphore.acquire();
System.out.println("顧客" + finalI + "在挑選商品,購買...");
// 假設挑選了xx長時間,購買了
Thread.sleep(1000);
// 歸還一個許可,後邊的就可以進來購買了
System.out.println("顧客" + finalI + "購買完畢了...");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
輸出結果:
反正每次只能5個客戶同時進酸奶小店購買挑選。
參考資料:
- https://blog.csdn.net/qq_19431333/article/details/70212663
- https://blog.csdn.net/panweiwei1994/article/details/78827248
四、總結
Java為我們提供了三個同步工具類:
- CountDownLatch(閉鎖)
- 某個線程等待其他線程執行完畢後,它才執行(其他線程等待某個線程執行完畢後,它才執行)
- CyclicBarrier(柵欄)
- 一組線程互相等待至某個狀態,這組線程再同時執行。
- Semaphore(信號量)
- 控制一組線程同時執行。
本文簡單的介紹了一下這三個同步工具類是幹嘛用的,要深入還得看源碼或者借鑒其他的資料。
最後補充一下之前的思維導圖知識點:
參考資料:
- 《Java併發編程實戰》
- http://www.cnblogs.com/dolphin0520/p/3920397.html
- https://zhuanlan.zhihu.com/p/27829595
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關註微信公眾號:Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
文章的目錄導航: