本人編寫這篇文章主要是爲了記錄本人的學習心得,理解上可能會有偏頗。儘量簡短的去總結各個功能的特點。方便以後去回顧。也真心的希望本人的一些淺薄的見解能夠得到大家的認可。
本人以下內容如果有錯誤和不足希望各位大神大佬們看見了後留言指出,本人將完善修改,儘量不去誤解大家!!
Java多線程以及併發是作爲一個Java開發程序猿是必須要掌握的一項知識點。之前也是一知半解,也是希望通過這篇文章把自己的理解經過記錄下來。
首先,Java官方提供了對於多線程處理的juc包(全路徑java.util.concurrent)。大家想要深入瞭解的話,可以去這個包下去仔細看下。
下面來說下CountDownLatch、CyclicBarrier、Semaphore這三個類:
一、作用:這三個類都可以實現在多線程情況下,控制併發數量。原理都是遵循AQS原理。
二、特點:
1、CountDownLatch 倒計數器,當計數器爲0時,所有線程釋放。子線程數量可以不等於設置的數量。計數器減小隻需調用countDown()即可。不限制必須在子線程調用此方法。標紅的字需要重點記憶一下,這是很重要的一個特點。
第一種情況:線程數和閾值是相同的,每個線程完成後調用countdown方法
private static void test3() {
//實例化個CountDownLatch 設置計數器閾值
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
System.out.println("thread1 run start");
Thread.sleep(1000);
System.out.println("thread1 sleep 5000");
latch.countDown();
System.out.println("thread1 over");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
System.out.println("thread2 run start");
Thread.sleep(1000);
System.out.println("thread2 sleep 1000");
latch.countDown();
System.out.println("thread2 over");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("all thread over");
}
打印下:
thread1 run start
thread2 run start
thread1 sleep 5000
thread1 over
thread2 sleep 1000
thread2 over
all thread over
第二種情況:只有一個線程的情況下,在線程內兩次調用countdown方法來釋放
private static void test4() {
//實例化個CountDownLatch 設置計數器閾值
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
System.out.println("thread1 run start");
System.out.println("thread1 sleep 500");
Thread.sleep(500);
System.out.println("thread1 over");
latch.countDown();
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("all thread over");
}
打印下結果
thread1 run start
thread1 sleep 500
thread1 over
all thread over
從打印結果看,這種情況也是沒問題的。
第三種情況,countdown方法在主線程調用
private static void test5() {
//實例化個CountDownLatch 設置計數器閾值
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
System.out.println("thread1 run start");
System.out.println("thread1 sleep 500");
Thread.sleep(500);
System.out.println("thread1 over");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
latch.countDown();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("all thread over");
}
打印下結果:
thread1 run start
thread1 sleep 500
thread1 over
all thread over
總結一下,CountDownLatch類只需要把定義好的閾值減少到0即可,調用countdown方法的數量等於閾值即可。而具體countdown方法是在哪裏調用並沒有嚴格的限制。
2、CyclicBarrier 特點:計數器,當計數器從0加到閾值後釋放。
CyclicBarrier可以使一定數量的線程反覆地在柵欄位置處彙集。當線程到達柵欄位置時將調用await方法,這個方法將阻塞直到所有線程都到達柵欄位置。如果所有線程都到達柵欄位置,那麼柵欄將打開,此時所有的線程都將被釋放,而柵欄將被重置以便下次使用。CyclicBarrier自身並不保證主線程在子線程完成之後執行。構造方法中的第二個參數就是最後一個到達的線程
應用場景:再不影響主線程運行的場景下,並且可重複
直接複製一下他人的
private static void test6() {
int threadCount = 3;
CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount,()->{
System.out.println("asdfasdf");
});
for (int i = 0; i < threadCount; i++) {
System.out.println("創建工作線程" + i);
Worker worker = new Worker(cyclicBarrier);
worker.start();
}
System.out.println("all thread over");
}
// 自定義工作線程
private static class Worker extends Thread {
private CyclicBarrier cyclicBarrier;
public Worker(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
super.run();
try {
System.out.println(Thread.currentThread().getName() + "開始等待其他線程");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "開始執行");
// 工作線程開始處理,這裏用Thread.sleep()來模擬業務處理
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "執行完畢");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、Semaphore 信號量爲多線程協作提供更強大的控制。從廣義上來講,是鎖的增強,synchronized、ReentrantLock只能對一個線程加鎖,而信號量可以對多個線程進行控制。
應用場景:接口訪問限流