Java併發之信號量、倒計數門閂、循環柵欄和LockSupport

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("執行結束了");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章