Java中的基礎構建模塊(第五章)

Java中的基礎構建模塊

Java平臺類庫包含了豐富的併發基礎構建模塊,例如線程安全的容器類以及各類用於協調多個相互協做的線程控制流的同步工具類。安全

1.同步容器類

同步容器類都是線程安全的,但在某些狀況下可能須要額外的客戶端加鎖來保護複合操做。常見的複合操做包括:迭代、跳轉(在容器內元素之間)、條件運算(例如「若沒有則添加」)。併發

隱式迭代:某些狀況下迭代操做會隱藏起來。以下代碼中println調用Set的toString方法,而後對Set中的對象進行迭代調用toString方法:dom

public class HiddenIterator {
	private final Set<Integer> set = new HashSet<Integer>();
    public void addTenThings() {
    	Random r = new Random();
        for (int i = 0;i < 10;i++) add(r.nextInt());
        System.out.println("Debug : " + set);
    }
}

2.併發容器類

併發容器專門針對多個線程併發訪問設計。 經過使用併發容器代替同步容器,能夠極大提升伸縮性並下降風險。工具

  1. ConcurrentHashMap:替代同步的基於散列Map,在其接口中增長了一些常見符合操做的支持,如「若沒有則添加」、替換有條件刪除等。 ConcurrentHashMap使用了一種粒度更細的加鎖機制:分段鎖來實現更大程度的共享,可以在併發環境下提升吞吐量。 ConcurrentHashMap不能被加鎖來執行獨佔訪問。 在實際使用中,只有當應用程序須要加鎖以進行獨佔訪問時,才應該放棄使用ConcurrentHashMap。

ConcurrentHashMap中的原子操做:性能

方法 說明
v putIfAbsent(K key,V value) 僅當k沒有相應的映射值時才插入
boolean remove(K key,V value) 僅當k被映射到v時才移除
boolean replace(K key,V oldValue,V newValue) 僅當k被映射到oldValue時才進行替換
V replace(K key,V newValue) 僅當k被映射到某個值時才進行替換
  1. CopyOnWriteArrayLIst:代替同步List,提供了更好的併發性能,而且在迭代期間不須要對容器進行加鎖或複製。 「寫入時複製(Copy-On-Write)」容器的線程安全性在於,只要正確地發佈一個事實不可變對象,那麼在訪問該對象時就不須要進一步的控制,在每次修改時,都會建立併發佈一個新的容器副本,從而實現可變性。 僅當迭代操做遠遠多於修改操做時,才應該使用「寫入時複製」容器。.net

  2. BlockingQueue(生產者----消費者):相對於Queue,增長了可阻塞的插入和獲取等操做。若是隊列爲空,那麼獲取元素的操做將一直阻塞,直到隊列中出現一個可用的元素,若是隊列已滿(對於有界隊列),那麼插入操做將會一直阻塞,直到隊列中出現可用空間。 阻塞隊列提供了可阻塞的take和put方法,以及支持定時的offer和poll方法。 在構建高可靠的應用程序時,有界隊列是一種強大的資源管理工具:它們能抑制併產生過多的工做項,使應用程序在負荷過載的狀況下變得更加健壯。因此應該經過阻塞隊列在設計中構建資源管理機制。線程

    • LinkedBlockingQueue
    • ArrayBlockingQueue
    • PriorityBlockingQueue

串行線程封閉:線程封閉對象只能由一個線程擁有,但能夠經過安全地發佈該對象來「轉移」全部權,在轉移全部權後,也只有另外一個線程能得到這個對象的訪問權限,而且發佈對象的線程不會再訪問它。設計

  1. 雙端隊列 Deque---->BlockingDeque---->ArrayBlockingQueue、LinkedBlockingQueue

3.阻塞與中斷

線程阻塞的緣由: - 等待I/O操做結束 - 等待得到一個鎖 - 等待從sleep方法醒來 - 等待另外一個線程的計算結果code

Thread.interrupt()用於中斷線程。Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,在線程受到阻塞時拋出一箇中斷信號,這樣線程就得以退出阻塞的狀態。更確切的說,若是線程被Object.wait, Thread.join和 Thread.sleep三種方法之一阻塞,那麼,它將接收到一箇中斷異常(InterruptedException),從而提前地終結被阻塞狀態。對象

所以,若是線程被上述幾種方法阻塞,正確的中止線程方式是設置共享變量,並調用interrupt()(注意變量應該先設置)。若是線程沒有被阻塞,這時調用interrupt()將不起做用;不然,線程就將獲得異常(該線程必須事先預備好處理此情況),接着逃離阻塞狀態。在任何一種狀況中,最後線程都將檢查共享變量而後再中止。

4.同步工具類

同步工具類能夠是任何一個對象,只要它根據其自身的狀態來協調線程的控制流。阻塞隊列能夠做爲同步工具類,其餘類型的同步工具類還包括信號量(Semaphore)柵欄(Barrier)以及閉鎖(Latch)

  • 閉鎖(Latch):閉鎖是一種同步工具類,能夠延遲線程的進度直到其到達終止狀態。閉鎖的做用至關於一扇門:在閉鎖到達結束狀態以前,這扇門一直是關閉的,而且沒有任何線程可以經過,當到達結束狀態時,這扇門會打開並容許全部的線程經過。當閉鎖到達結束狀態後,將不會再改變狀態,所以這扇門將永遠保持打開狀態。 閉鎖能夠用來確保某些活動直到其餘活動都完成後才繼續執行,如:
    1. 確保某個計算在其須要的全部資源都被初始化以後才繼續執行
    2. 確保某個服務在其依賴的全部其餘服務都已經啓動以後才啓動
    3. 等待直到某個操做的全部參與者(如在多玩家遊戲中的全部玩家)都就緒再繼續執行。

Java提供了閉鎖的實現:CountDownLatch。它可使一個或多個線程等待一組時間發生。閉鎖狀態包括一個計數器,該計數器被初始化爲一個正數,表示須要等待的事件數量。countDown()方法遞減計數器,表示有一個時間已經發生了;await()方法等待計數器到達0,表示全部須要等待的事件都已經發生,若是計數器非0,await會一直阻塞直到計數器爲0,或等待中的線程中斷、超時。

  • FutureTask FutureTask實現了Future語義,表示一種抽象的可生成結果的計算。Future.get的行爲取決於任務的狀態,若是任務已經完成,那麼get會當即返回結果,不然get將阻塞直到任務進入完成狀態,而後返回結果或者拋出異常。 FutureTask表示的計算是經過Callable來實現的,至關於一種可生成結果的Runnable,而且能夠處於如下3種狀態:等待運行(Waiting to run)、正在運行(Running)和運行完成(Completed)。 關於FutureTask的詳細信息能夠參看blog: Java中建立線程的方法

  • 信號量 計數信號量(Counting Semaphore)用來控制同時訪問某個特定資源的操做數量,或者同時執行某個指定操做的數量,計數信號量還能夠用來實現某種資源池,或者對容器施加邊界。

  • 柵欄 柵欄相似於閉鎖,它能則色一組線程直到某個事件發生。柵欄與閉鎖的關鍵區別在於:全部線程必須同時到達柵欄位置,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其餘線程。若是全部線程都達到了柵欄位置,那麼柵欄將打開,此時全部的線程都被釋放,而柵欄將被重置以便下次使用。 Java中提供CyclicBarrier實現柵欄功能。

相關文章
相關標籤/搜索