java 併發工具類

在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
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章