同步容器并發(fā)容器

同步容器類

  • Vector、Hashtable,及由Collections.synchronizedXxx等工程方法創(chuàng)建的類。
  • 這些類實現(xiàn)線程安全方式是:將它們的狀態(tài)封裝起來,并對每個公有方法都進行同步,使得每次只有一個線程能方法容器狀態(tài)。

同步容器類的問題

  • 客戶端新建的復(fù)合操作需要通過容器類的鎖來保證原子性。
  • 在迭代操作中,多線程情況下回出現(xiàn)ArrayIndexOutOfBoundsException異常,如果要解決,則需要在客戶端加鎖,但是犧牲了一些伸縮性。

迭代器與CncurrrentModificationException

  • 發(fā)現(xiàn)容器在迭代過程中被修改過,會拋出CME異常。實現(xiàn)原理:將容器計數(shù)器與容器關(guān)聯(lián)起來:如果在迭代期間計數(shù)器被修改,那么hasNext或next將拋出CME異常。然后,這種檢查在沒有同步的情況下進行的,因此可能看到失效的計數(shù)值:ABA問題。

隱藏的迭代器

  • 容器的toString、hashCode、equals、containsAll、removeAll、retainAll以及包容器作為參數(shù)的構(gòu)造函數(shù)都會對容器進行迭代,在并發(fā)下就可能出現(xiàn)CME異常。所以在并發(fā)情況下,在迭代期間需要對容器加鎖。

并發(fā)容器

  • 并發(fā)容器為了改變同步容器的性能,同步容器對容器狀態(tài)的訪問時串行化,以實現(xiàn)線程安全,這種方法降低了并發(fā)性,和吞吐量。
  • 并發(fā)容器針對多線程并發(fā)訪問設(shè)計的,如果需要提高并發(fā)性使用以下替代方案。
  • ConcurrentHashMap代替同步且散列的Map.
  • CopyOnWriteArrayList用于在遍歷操作為主要操作的情況下代替同步的List。
  • ConcurrentMap接口中增加了常見復(fù)合操作的支持:“若沒有則添加”、替換、有條件刪除。
  • ConcurrentSkipListMap代替SortedMap
  • ConcurrentSkipListSet代替SortedSet
    *并發(fā)容器類提供的迭代器不會拋出CME異常。

Queue、BlockingQueue容器接口

  • Queue用來臨時保存一組待處理元素,實現(xiàn)類包括:ConcurrentLinkedQueue(先進先出隊列)、PriorityQueue(優(yōu)先隊列,非并發(fā))
  • Queue上的操作不會阻塞,如果隊列為空,返回空值。
  • Queue通過LinkedList來實現(xiàn)的。
  • BlockingQueue擴展Queue,增加了可阻塞的插入和獲取等操作,如果隊列為空,那么獲取元素操作將一直阻塞,直到隊列中出現(xiàn)一個可用元素。插入時,如果隊列已滿(對于有界隊列來說,無界隊列除外),插入操作將一直阻塞,知道隊列中出現(xiàn)可用空間。適合“生產(chǎn)者-消費者模式”
  • BlockingQueue提供了offer方法,當隊列滿時offer添加元素失敗返回false,此時可以做相應(yīng)策略調(diào)整(減少生產(chǎn)線程或序列化數(shù)據(jù)到磁盤),同樣也提供了poll方法,在一定時間內(nèi)如果沒有獲取到元素則返回null。

ConcurrentHashMap

  • 使用分段鎖(Lock Striping)來提高并發(fā)性和伸縮性。
  • size和isEmpty返回的結(jié)果可能會過期,實際上他只是一個估值,但這兩個方法用的不多。
  • 在CHM中沒有實現(xiàn)對Map加鎖以提供獨占方法,在Hashtable、synchronizedMap中獲得Map的鎖能防止其他線程訪問這個Map
  • 與Hashtable、synchronizedMap相比,CHM有更多優(yōu)勢,大多數(shù)情況下使用CHM來提高代碼的伸縮性,只有當程序需要加鎖Map以進行獨占訪問,或者需要依賴同步Map帶來的一些其他作用是才應(yīng)該放棄CHM.

CopyOnWriteArrayList、CopyOnWriteArraySet

  • 寫入時復(fù)制容器,在修改的時候會創(chuàng)建并發(fā)布一個新的容器副本。
  • 修改方法(刪除、添加)是加鎖的,避免多個線程調(diào)用產(chǎn)生多個副本。
  • 修改時會復(fù)制底層數(shù)組,所以需要一定開銷,所以適合讀遠大于寫的業(yè)務(wù)場景。
    Java并發(fā)編程:并發(fā)容器之CopyOnWriteArrayList(轉(zhuǎn)載)

阻塞隊列和生產(chǎn)者-消費者模式

  • BlockingQueue的多種實現(xiàn):LinkedBlockingQueue、ArrayBlockingQueue是FIFO隊列。PriorityBlockingQueue是按優(yōu)先級排序隊列,即可根據(jù)元組自然排序來比較元素(如果他們實現(xiàn)Comparable方法),也可以使用Comparator來比較。

雙端隊列

  • Deque(發(fā)音“deck”)和BlockingDeque對Queue、BlockingQueue進行了擴展。Deque是一個雙端隊列,實現(xiàn)了在隊列頭和尾高效插入和移除。
  • 實現(xiàn)為ArrayDeque、LinkedBlockingQueue

阻塞方法與中斷方法

  • 阻塞:等待I/O操作結(jié)束、等待獲取一個鎖,等待從Thread.sleep方法中醒來、等待另一個線程的計算結(jié)果。阻塞線程處于阻塞狀態(tài),等待某個不受它控制的事件發(fā)生后才能繼續(xù)執(zhí)行,當外部事件發(fā)生時,線程被置回Runnable(就緒狀態(tài))。

  • LinkedBlockingQueue的take、put等方法會拋出InterruptedException與Thread.sleep一樣。當方法拋出InterruptedException異常,表示該方法時一個阻塞方法,如果這個方法被中斷,那么它將努力提前結(jié)束阻塞狀態(tài)。(只是努力不是保證)

  • LinkedBlockingQueue的take阻塞方法源碼

 /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }
  • 中斷,Thread類提供了interrupt方法,用于中斷線程或者查詢線程是否已經(jīng)被中斷。每個線程都有一個布爾類型的屬性,標識線程的中斷狀態(tài),當中斷線程時將設(shè)置這個狀態(tài)。
  • 中斷是一種協(xié)作機制,一個線程不能強制其它線程停止正在執(zhí)行的操作而去執(zhí)行其它的操作。
  • A線程中斷B時,A僅僅是要求B在執(zhí)行到某個可以暫停的地方停止正在執(zhí)行的動作---前提是如果線程B愿意停止下來。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容