1、CountDownLatch
1.應用場景
在實際多線程併發開發過程當中,咱們會遇見不少等待子線程完畢後在繼續執行的狀況,(如多個子線程下載文件,全部子線程執行完畢後再重命名爲文件名)。
2.使用方式
CountDownLatch的構造函數接受一個int類型的參數做爲計數器,調用countDwon()方法,計數器減1,await()方法阻塞當前線程,直到計數器變爲0;、
補充:
計數器爲0的時候,調用awaite()方法不會阻塞主線程;
初始化後,不能修改計數器的值;
可使用await(long time,TimeUnit unit)等待特定時間後,就不阻塞主線程; java
3.實例代碼數據庫
public class Main { //等待2個子線程執行完畢,計數器爲2 static CountDownLatch countDownLatch = new CountDownLatch(2); public static void main(String[] args) { System.out.println("start subThread doing..."); //建立並開啓2個子線程 SubThread subThread1 = new SubThread(); SubThread subThread2 = new SubThread(); subThread1.start(); subThread2.start(); try { //阻塞主線程,等待子線程結束 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("subThread are finish..."); } static class SubThread extends Thread { @Override public void run() { //模擬執行任務 try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //子線程執行完畢,減小計數器 System.out.println(getName() + " done..."); countDownLatch.countDown(); } } }
運行結果:當Thread-一、Thread-0兩個子線程執行完畢後,在運行main線程後續的邏輯 多線程
start subThread doing... Thread-1 done... Thread-0 done... subThread are finish...
2、CyclicBarrier
1.應用場景
若是當你碰見須要讓一組線程達到同一個屏障(同步點)時被阻塞,直到最後一個線程達到屏障時,屏障纔會打開的狀況。
2.使用方式
CycliBarrier默認的構造方法CyclicBarrier(int parties),參數標識屏障攔截的線程個數,每一個線程調用await()方法告訴SyclicBarrier咱們已經達到屏障了,而後當前線程被阻塞。當全部子線程都達到屏障後,則繼續執行子線程的後續邏輯。
補充:
CyclicBarrier還提供了一個更高級的函數CyclicBarrier(int parties,Runnable barrierAction),用於在線程達到屏障時,優先執行barrierAction。
3.實例代碼 併發
public class Main { //攔截2個子線程屏障 static CyclicBarrier cyclicBarrier = new CyclicBarrier(2); public static void main(String[] args) { System.out.println("start subThread doing..."); SubThread subThread1 = new SubThread(); SubThread subThread2 = new SubThread(); subThread1.start(); subThread2.start(); } static class SubThread extends Thread { @Override public void run() { try { System.out.println(getName() + " doing first things."); //模擬子線程執行第一個任務 sleep(3000); System.out.println(getName() + " done first things."); } catch (InterruptedException e) { e.printStackTrace(); } try { //完成第一個任務,告知達到屏障 cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } //全部子線程都完成第一個任務後,繼續運行每一個子線程的下一個任務 System.out.println(getName() + " doing other things."); } } }
運行結果:當子線程都執行完第一個任務到達屏障後,執行下一個任務 ide
start subThread doing... Thread-0 doing first things. Thread-1 doing first things. Thread-1 done first things. Thread-0 done first things. Thread-0 doing other things. Thread-1 doing other things.
3、Semaphore
1.應用場景
多線程訪問公共資源的狀況在開發過程當中常常碰見,如數據庫鏈接,可能開啓幾十個線程進行併發讀取,可是考慮到數據庫鏈接性能和消耗,咱們必須控制10個線程哪一個是鏈接數據庫。Semaphore就是用來控制同時訪問特定資源的線程數量。
2.使用方式
Semaphore的構造方法Semaphore(int permits),permits標識許可證數量。執行任務前,acquire()方法獲取一個許可證;任務執行完成後調用relese()方法歸還許可證。沒有得到許可證的子線程就阻塞等待。
補充:
tryAcquire():嘗試獲取許可證;
intavaliablePermits():返回信號量中當前許可證的個數;
intgetQueueLength():返回正在等待獲取許可證的線程個數;
booleanhasQueueThreads():是否有線程正在等待許可證;
reducePermits(int reduction):減小reduction個許可證;
getQueuedThreads():返回全部等待獲取許可證的線程集合;
3.實例代碼 函數
public class Main { //建立2個許可證 static Semaphore semaphore = new Semaphore(2); public static void main(String[] args) { System.out.println("start subThread doing..."); //同時開啓4個子線程運行 for (int i = 0; i < 4; i++) { SubThread subThread = new SubThread(); subThread.start(); } } static class SubThread extends Thread { @Override public void run() { try { //執行任務前獲取許可證 semaphore.acquire(); System.out.println(getName() + "doing things."); sleep(3000); //執行完任務釋放許可證 semaphore.release(); System.out.println(getName() + "finish things."); } catch (InterruptedException e) { e.printStackTrace(); } } } }
運行結果:同時只有2個線程運行,當某個線程運行完畢釋放許可後,下一個線程才獲取許可運行; 性能
start subThread doing... Thread-0doing things. Thread-1doing things. Thread-1finish things. Thread-2doing things. Thread-0finish things. Thread-3doing things. Thread-2finish things. Thread-3finish things.
4、Exchanger
1.應用場景
在某些實際業務如流水錄入中,爲了不錯誤。採用兩我的同時錄入,並對比錄入的結果是否一致。Exchanger用於進行線程之間的數據交換,它提供了一個同步點,兩個線程能夠交換彼此的數據。
2.使用方式
兩個線程經過exchange()方法交換數據,若是一個線程執行exchange()方法,它會一直等待第二個線程也執行exchange()方法。當兩個線程都達到同步點時,就能夠交換數據,將本線程產生的數據傳遞給對方。
3.實例代碼 ui
public class Main { //用戶線程間交換數據(String)對象exchanger static Exchanger<String> exchanger = new Exchanger<>(); public static void main(String[] args) { //建立2個子線程分別執行 SubThread1 subThread1 = new SubThread1(); SubThread2 subThread2 = new SubThread2(); subThread1.start(); subThread2.start(); } static class SubThread1 extends Thread { @Override public void run() { try { System.out.println(getName() + "start doing..."); //模擬執行完成後,獲取結果result1,並將result1交換給對方線程 sleep(3000); String result1 = "3000"; String result2 = exchanger.exchange(result1); //待兩個線程都執行完畢後,交換數據進行比較 System.out.println(getName() + " thread1 result:" + result1 + " is equals thread2 result:" + result2 + "," + result1.equals(result2)); } catch (InterruptedException e) { e.printStackTrace(); } } } static class SubThread2 extends Thread { @Override public void run() { try { System.out.println(getName() + "start doing..."); //模擬執行完成後,獲取結果result2,並將result2交換給對方線程 sleep(2000); String result2 = "2000"; String result1 = exchanger.exchange(result2); //待兩個線程都執行完畢後,交換數據進行比較 System.out.println(getName() + " thread1 result:" + result1 + " is equals thread2 result:" + result2 + "," + result1.equals(result2)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
運行結果:線程1優先執行完畢,等待線程0執行完畢後,交換數據分別進行結果比較 線程
Thread-1start doing... Thread-0start doing... Thread-1finish doing... Thread-0finish doing... Thread-0 thread1 result:3000 is equals thread2 result:2000,false Thread-1 thread1 result:3000 is equals thread2 result:2000,false