第19講 Java併發包提供了哪些併發工具類?

Java併發包提供了哪些併發工具類?

我們通常所說的併發包也就是java.util.concurrent及其子包,集中了Java併發的各種基礎工具類,具體主要包括幾個方面:

  • 提供了比synchronized更加高級的各種同步結構,包括CountDownLatch、 CyclicBarrier、 Semaphore等,可以實現更加豐富的多線程操作,比如利用Semaphore作爲資源
    控制器,限制同時進行工作的線程數量。
  • 各種線程安全的容器,比如最常見的ConcurrentHashMap、有序的ConcunrrentSkipListMap,或者通過類似快照機制,實現線程安全的動態數
    組CopyOnWriteArrayList等。
  • 各種併發隊列實現,如各種BlockedQueue實現,比較典型的ArrayBlockingQueue、 SynchorousQueue或針對特定場景的PriorityBlockingQueue等
  • 強大的Executor框架,可以創建各種不同類型的線程池,調度任務運行等,絕大部分情況下,不再需要自己從頭實現線程池和任務調度器。

從jdk文檔中我們看到:
java8jdk在線
主要有3個包

java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks

這三個包中包含了上述的回答。
作者文章中主要關注了3個點,剩下的後面會有提到。

  • CountDownLatch,允許一個或多個線程等待某些操作完成
  • CyclicBarrier,一種輔助性的同步結構,允許多個線程等待到達某個屏障。
  • Semaphore, Java版本的信號量實現。

Semaphore

基本API

Semaphore(int permits):構造方法

Semaphore(int permits,boolean fair):構造方法,當fair等於true時,創建具有給定許可數的計數信號量並設置爲公平信號量。

void acquire():從此信號量獲取一個許可前線程將一直阻塞。或線程爲 interrupted 。

acquire(int permits):每調用一次此方法,就獲得permits個許可。

release():釋放許可證,將其返回到信號量。

release(int permits):釋放給定數量(permits)的許可證,將其返回到信號量。

int availablePermits():返回此信號量中當前可用的許可數。

Semaphore使用

4步

	//創建信號量
   Semaphore semaphore = new Semaphore(5);
   //創建線程
   new Thread(new SemaphoreWorker(semaphore));
   //獲取
     semaphore.acquire();
     //釋放
    semaphore.release();

Semaphore情景:

隊伍一次進來5個人上車,等這5個人坐車出發,再放進去下一批

public class UsualSemaphoreSample {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Action...GO!");
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new SemaphoreWorker(semaphore));
            t.start();
        }
    }
}
class SemaphoreWorker implements Runnable {
    private String name;
    private Semaphore semaphore;
    public SemaphoreWorker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            log("is waiting for a permit!");
            semaphore.acquire();
            log("acquired a permit!");
            log("executed!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            log("released a permit!");
            semaphore.release();
        }
    }
    private void log(String msg){
        if (name == null) {
            name = Thread.currentThread().getName();
        }
        System.out.println(name + " " + msg);
    }
}

結果:

Action...GO!
Thread-0 is waiting for a permit!
Thread-2 is waiting for a permit!
Thread-1 is waiting for a permit!
Thread-1 acquired a permit!
Thread-1 executed!
Thread-1 released a permit!
Thread-0 acquired a permit!
Thread-0 executed!
Thread-2 acquired a permit!
Thread-0 released a permit!
Thread-3 is waiting for a permit!
Thread-2 executed!
Thread-2 released a permit!
Thread-3 acquired a permit!
Thread-3 executed!
Thread-3 released a permit!
Thread-5 is waiting for a permit!
Thread-5 acquired a permit!
Thread-5 executed!
Thread-5 released a permit!
Thread-6 is waiting for a permit!
Thread-6 acquired a permit!
Thread-6 executed!
Thread-6 released a permit!
Thread-4 is waiting for a permit!
Thread-4 acquired a permit!
Thread-4 executed!
Thread-4 released a permit!
Thread-7 is waiting for a permit!
Thread-7 acquired a permit!
Thread-7 executed!
Thread-7 released a permit!
Thread-8 is waiting for a permit!
Thread-8 acquired a permit!
Thread-8 executed!
Thread-8 released a permit!
Thread-9 is waiting for a permit!
Thread-9 acquired a permit!
Thread-9 executed!
Thread-9 released a permit!

Process finished with exit code 0

線程試圖獲得工作允許,得到許可則進行任務,然後釋放許可,這時等待許可的其他線程,就可獲得許可進入工作狀態,直到全部處理結束。

缺點:
一直有5個人可以試圖乘車,如果有1個人出發了,立即就有排隊的人獲得
許可,而這並不完全符合我們前面的要求

修改


public class AbnormalSemaphoreSample {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(0);
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new MyWorker(semaphore));
            t.start();
        }
        System.out.println("Action...GO!");
        semaphore.release(5);
        System.out.println("Wait for permits of");
        while (semaphore.availablePermits()!=0) {
            Thread.sleep(100L);
        }
        System.out.println("Action...GO again!");
        semaphore.release(5);
    }
}


class MyWorker implements Runnable {
    private Semaphore semaphore;
    public MyWorker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println("Executed!"+ Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

結果:

Action...GO!
Wait for permits of
Executed!Thread-1
Executed!Thread-2
Executed!Thread-0
Executed!Thread-3
Executed!Thread-4
Action...GO again!
Executed!Thread-7
Executed!Thread-8
Executed!Thread-5
Executed!Thread-9
Executed!Thread-6

Process finished with exit code 0

CountDownLatch和CyclicBarrier的不同點

  • CountDownLatch是不可以重置的,所以無法重用;而CyclicBarrier則沒有這種限制,可以重用
  • CountDownLatch的基本操作組合是countDown/await。調用await的線程阻塞等待countDown足夠的次數,不管你是在一個線程還是多個線程裏countDown,只要次數足夠
    即可。所以就像Brain Goetz說過的, CountDownLatch操作的是事件。
  • CyclicBarrier的基本操作組合,則就是await,當所有的夥伴(parties)都調用了await,纔會繼續進行任務,並自動進行重置。 注意,正常情況下, CyclicBarrier的重置都是自
    動發生的,如果我們調用reset方法,但還有線程在等待,就會導致等待線程被打擾,拋出BrokenBarrierException異常。 CyclicBarrier側重點是線程,而不是調用事件,它的
    典型應用場景是用來等待併發線程結束。

CountDownLatch說明

上面的區別作者說完之後,對於我從來沒有用過的人來說還是不明白什麼意思。

主要補充以下問題。

  • 判斷count不爲0的時,則當前線程呈wait狀態。

  • await的線程阻塞等待countDown足夠的次數(次數由構造方法提供),下面的次數爲6。

//創建6個CountDownLatch類對象
 CountDownLatch latch = new CountDownLatch(6);
  • countDown()方法,此方法將CountDownLatch(6)中6 減1
  • await()方法 :當前線程等待,直到 減少到0.
  • getCount()方法:通常用來測試用的,當前計數的值。

再看下面的代碼會好些。

CountDownLatch情景

假設有10個人排隊,我們將其分成5個人一批,一共兩個批次,通過CountDownLatch來協調批次。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchSample {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(6);
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new FirstBatchWorker(latch));
            t.start();
        }
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new SecondBatchWorker(latch));
            t.start();
        }

        // 注意這裏也是演示目的的邏輯,並不是推薦的協調方式
        while ( latch.getCount() != 1 ){
            Thread.sleep(100L);
        }
        System.out.println("Wait for first batch finish");
        latch.countDown();
    }
}
public class FirstBatchWorker implements Runnable {
    private CountDownLatch latch;

    public FirstBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.println("First batch executed!"+Thread.currentThread().getName());
        latch.countDown();
            //每次countDown之後都會少1
        System.out.println("First batch getCount"+ latch.getCount());
    }
}
import java.util.concurrent.CountDownLatch;

public class SecondBatchWorker implements Runnable {

    private CountDownLatch latch;

    public SecondBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            latch.await();
            System.out.println("Second batch executed!"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

結果:

First batch executed!Thread-1
First batch getCount5
First batch executed!Thread-2
First batch executed!Thread-0
First batch getCount4
First batch getCount3
First batch executed!Thread-4
First batch getCount2
First batch executed!Thread-3
First batch getCount1
Wait for first batch finish
Second batch executed!Thread-8
Second batch executed!Thread-9
Second batch executed!Thread-5
Second batch executed!Thread-7
Second batch executed!Thread-6

Process finished with exit code 0

CyclicBarrier

作者說完之後還是不明白。。。自我總結如下:

  • 從單詞上看,是循環,屏障的意思。不同點從字面上看出來了,CyclicBarrier是循環,CountDownLatch無法重用。

  • CyclicBarrier計數爲加法。

  • 構造函數:CyclicBarrier(int parties, Runnable barrierAction)

    • parties:是參與線程的個數,不能小於1
    • Runnable :最後一個到達線程要做的任務
  • await()方法:線程調用 await() 表示自己已經到達Barrier

  • 下面代碼5個線程都執行了await()方法都執行了,才執行下面的方法。否則線程彼此等待,一直呈阻塞狀態。

        CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("Action...GO again!");
            }
        });

在這裏插入圖片描述

CyclicBarrier情景

5個工作線程其實更像是代表了5個可以就緒空車,而不再是5個乘客,對比前面CountDownLatch的例子更有助於我們區別它們的抽象模型。


public class CyclicBarrierSample {
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
            //5個線程執行了纔會去執行此方法。
                System.out.println("Action...GO again!");
            }
        });
        //5個線程
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new CyclicWorker(barrier));
            t.start();
        }
    }


    static class CyclicWorker implements Runnable {
        private CyclicBarrier barrier;

        public CyclicWorker(CyclicBarrier barrier) {
            this.barrier = barrier;
        }

        @Override
        public void run() {
            try {
            	//結果循環了3次 自動重置
                for (int i = 0; i < 3; i++) {
                    System.out.println(Thread.currentThread().getName()+" Executed!");
                    barrier.await();
                }

            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


結果:

Thread-1 Executed!
Thread-0 Executed!
Thread-2 Executed!
Thread-3 Executed!
Thread-4 Executed!
Action...GO again!
Thread-4 Executed!
Thread-2 Executed!
Thread-1 Executed!
Thread-3 Executed!
Thread-0 Executed!
Action...GO again!
Thread-0 Executed!
Thread-4 Executed!
Thread-1 Executed!
Thread-3 Executed!
Thread-2 Executed!
Action...GO again!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章