在Java併發編程中我們往往需要一些工具類來實現我們的功能,JDK給我們提供了一些工具類,合理地使用它們能幫忙我們快速地完成功能。下面來學習一下這些工具類的使用吧!
一.等待多線程完成的CountDownLatch
CountDownLatch允許一個或多個線程等待其他線程完成操作。
假設有這樣一個需求,主線程需要等待其他幾個線程完成後再繼續執行。
一種方案是使用join方法。在主線程中調用其它線程的join方法,每調用一個join,如果該線程沒有結束主線程就會阻塞在這裏。直到該線程結束主線程才變爲運行態。關於join方法的介紹請參考Java併發之基礎知識的最後一部分內容。
這個需求還可以使用CountDownLatch來實現。
1.1 CountDownLatch用法介紹
//使用之前先構造一個CDL
//注意這裏的入參2。這個參數表示需要計數2次,這個CDL才結束
CountDownLatch c = new CountDownLatch(2);
//計數 這個例子中總共需要調用兩次countDown(),計數才結束
c.countDown()
//在主線程中調用,調用了這個方法後如果c沒有計數到0則阻塞,直到計數爲0,喚醒線程
c.await()
1.2 具體的例子
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
static CountDownLatch c = new CountDownLatch(2);
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("線程1開始執行!");
Thread.sleep(3000);
System.out.println("線程1結束執行!");
c.countDown();
} catch (InterruptedException e) {
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("線程2開始執行!");
Thread.sleep(5000);
System.out.println("線程2結束執行!");
c.countDown();
} catch (InterruptedException e) {
}
}
});
t1.start();
t2.start();
try {
System.out.println("開始等待線程1、2結束");
c.await();
System.out.println("線程1、2結束");
} catch (InterruptedException e) {
}
}
}
執行結果
開始等待線程1、2結束
線程2開始執行!
線程1開始執行!
線程1結束執行!
線程2結束執行!
線程1、2結束
二.同步屏障CycliBarrier
CycliBarrier這個單詞的字面意思是可循環使用的屏障的。它的功能是:讓一組線程到達一個屏障時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,所有被阻塞的線程才能執行下去。
Q1:怎麼算到達屏障?
線程主動調用await()方法
Q2:如何執行線程數?
構造方法裏指定
2.1 CycliBarrier的使用
//首先需要構造一個對象,指定線程總數
//這裏指定了參數3,只有3個線程調用了對象c的await()方法後,3個線程纔會執行下去。
CyclicBarrier c = new CyclicBarrier(3);
//在線程裏使用。最後一個線程調用了,所有線程被喚醒,進入運行態
c.await()
2.2 使用CycliBarrier實現主線程等待兩個線程執行完再執行
public class CycliBarrierDemo {
static CyclicBarrier c = new CyclicBarrier(3);
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("線程1開始執行!");
Thread.sleep(3000);
System.out.println("線程1結束執行!");
c.await();
} catch (Exception e) {
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("線程2開始執行!");
Thread.sleep(5000);
System.out.println("線程2結束執行!");
c.await();
} catch (Exception e) {
}
}
});
t1.start();
t2.start();
System.out.println("開始等待線程1、2結束");
c.await();
System.out.println("線程1、2結束");
}
}
執行結果同1.2
2.3其它
CyclicBarrier還提供了另一個更高級的構造方法CyclicBarrier(int parties,Runnable barrierAction),可以在所有線程到達屏障後優先執行指定的線程的run方法。
- Q1:這裏執行Runnable是新啓動一個線程嗎?or 直接調用run方法執行?
三.CyclicBarrier和CountDownLatch的區別
CountDownLatch的計數器只能使用一次,而CyclicBarrier的計數器可以使用reset()方法重置。
CB還提供了getNumberWating方法獲取阻塞的線程數量。isBroken方法用來了解阻塞的線程是否被中斷。一個線程被中斷後其他線程的await會拋出異常,這時調用isBroken方法可以輸出是否有線程被中斷。
另外調用線程的interrupt方法可以中斷線程。
四.控制併發線程數的Semaphore
Semaphore(信號量)是用來控制同時訪問特定資源的線程數量。
4.1基本使用
Semaphore 的構造方法需要傳入一個參數,如 new Semaphore(10),
表示最多有10個線程可以獲取到信號量,10個之後的線程再嘗試獲取時就會被阻塞。
獲取信號量使用:s.acquire()方法;
釋放信號量使用:s.release()方法。
只有前邊的線程釋放掉後,後面的線程(10個之後)才能被喚醒,重新獲取信號量。
它可以用來控制同時 運行的線程的數目。
4.2擴展API
intavailablePermits(); //返回此信號量中當前可用的許可證數
intgetQueueLength();//返回正在等待獲取許可證的線程數
booleanhasQueuedThreads();//是否有線程正在等待獲取許可證
void reducePermits(int reduction); //減少reduction個許可證,是個protected方法
Collection getQueuedThreads(); //返回所有等待獲取許可證的線程集合,是個protected方法
五.線程間交換數據的Exchanger
Exchanger可以用於線程間交換信息。它提供一個同步點,當兩個線程都到達這個同步點時,它們的信息交換。只有一個到達時,它先等待,直到另一個線程也到達。
4.1基本使用
Exchanger<String> e = new Exchanger<String>();是一個泛型類,泛型指明交換數據的類型。
e.exchange("hello")開始等待另一個線程也調用同一個對象的exchange
具體例子:
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
final Exchanger<String> exchanger = new Exchanger<String>();
threadPool.execute(new Runnable() {
public void run() {
try {
String b = exchanger.exchange("hello,anyone");
System.out.println("thread1 reviced :" + b);
} catch (Exception e) {
}
}
});
threadPool.execute(new Runnable() {
public void run() {
try {
String a = exchanger.exchange("hello,anybody");
System.out.println("thread2 reviced :" + a);
} catch (Exception e) {
}
}
});
}
Java併發之基礎知識
Java併發之volatile關鍵字
Java併發之synchronized關鍵字
Java併發之原子類
Java併發之線程池
Java併發之併發工具類
Java併發之AQS原理
Java併發之ThreadLocal使用和源碼分析
作者:第四單元
鏈接:https://www.jianshu.com/p/850e3282a80a
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。