1、信號量(Semaphore)
信號量(Semaphore)是對鎖的擴展,內部鎖synchronized和重入鎖ReentrantLock一次都只允許一個線程訪問一個資源,而信號量卻可以指定多個線程同時訪問某個資源。
// 指定準入數,即能同時申請多少個許可
public Semaphore(int permits)
// fair指定是否公平
public Semaphore(int permits, boolean fair)
主要邏輯方法
// 嘗試獲取一個准入許可,若無法獲得,則線程等待,直到有線程釋放一個許可或者當前線程被中斷
public void acquire();
// 不響應中斷
public void acquireUninterruptibly();
//嘗試獲取,不會進行等待,立即返回
public boolean tryAcquire();
public boolean tryAquire(long timeout, TimeUnit unit);
// 線程訪問資源結束後,釋放一個許可
public void release();
示例代碼
final Semaphore semap = new Semaphore(6);
public void run() {
try{
//申請一個信號量
semap.acquire();
doWork();
semap.release();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
2、倒計數門閂(CountDownLatch)
CountDownLatch用來控制線程的等待,它可以讓某個線程等待直到倒計時結束,再開始執行。
應用場景:比如流水線,後面的工序必須等到前面的工序做完之後才能繼續做。
構造方法如下:
// 指定計時器數量
public CountDownLatch(int count);
示例代碼
static final CountDownLatch latch = new CountDownLatch(5);
//子線程
public void run() {
try {
doWork();
latch.countDown(); //表示一個線程已經完成了一個流程任務,計時器減1
} catch(InterruptedException e) {
e.printStackTrace();
}
}
//主線程
public static void main(String[] args) {
ExecutorService exec = Excutors.newFixedThreadPool(5);
for(int i=0; i<5; i++) {
exec.submit(子線程);
}
//等待檢查直到 計數爲0
latch.await();
exec.shutdown();
}
3、循環柵欄(CyclicBarrier)
CyclicBarrier是CountDownLatch的升級版,功能更復雜強大
Cyclic意爲循環,就是說可以反覆使用。比如指定計數器爲10,當湊齊第一批10個線程後,計數器自動歸0;接着下一批…
應用場景:10個人一組一組的完成某個任務。
構造方法如下:
// parties爲計數總數,barrierAction 爲一次計數完成後的回調
public CyclicBarrier(int parties, Runnable barrierAction);
示例代碼
//子線程
public void run() {
try {
//等待所有線程到齊(達到計數),到齊後會第一次回調 barrierAction
cyclic.await();
doWork();
//進行下一輪計數
//等待所有線程 doWork 都做完,做完後會第二次回調 barrierAction
cyclic.await();
} catch(InterruptedException e) {
e.printStackTrace();
} catch(BrokenBarrierException e) {
e.printStackTrace();
}
}
對於BrokenBarrierException 異常,表示當前的柵欄CyclicBarrier已經破損了,可能系統已經沒有辦法等到所有的線程到齊了。如果繼續等待,可能就是白等。需進行異常處理。
4、線程阻塞工具類(LockSupport)
LockSupport 是一個阻塞工具,他可以在線程的任意位置阻塞線程。和Object.wait()相比,它不需要先獲得某個對象的鎖,不會拋出InterruptedException.
//阻塞當前線程
public static void park() {
UNSAFE.park(false, 0L);
}
//爲當前線程設置阻塞對象,阻塞對象會出現在線程Dump中
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
//定時阻塞
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
1、定時中斷
2、爲當前線程設置一個阻塞對象
3、處於park()掛起線程的狀態是WAITING狀態,使用suspend()掛起線程的狀態是Runnable狀態。
4、LockSuport.park() 支持中斷影響,但是不會拋出InterruptedException,只能通過Thread.interrupted() 等方法獲取中斷標記。
public void run() {
LockSupport.park();
if(Thread.interrupted()) {
System.out.println("線程被中斷了");
}
System.out.println("執行結束了");
}