併發容器 一 綜述

2021-10-03 23:00:39 字數 3923 閱讀 3103

四、阻塞佇列的實現原理

在開發過程中,經常會使用到一些資料結構,如雜湊表(hashmap)、佇列(queue)等,但是有一部分資料結構是執行緒不安全的,即在併發場景下,資料的獲取和儲存會出現問題。

為了解決這種問題,jdk提供了幾個併發安全的資料結構,如下圖所示:

由上圖可知,jdk中提供的併發容器有:

concurrenthashmap:分片加鎖實現高效能雜湊表。

concurrentlinkedqueue:採用cas演算法實現的非阻塞佇列。

blockingqueue:使用鎖物件實現的阻塞佇列。

實現乙個執行緒安全的佇列有兩種方式:

方式1:使用阻塞演算法,使用阻塞演算法的佇列可以用乙個鎖(入隊和出隊用同一把鎖)或兩個鎖(入隊和出隊用不同的鎖)等方式來實現。

方式2:使用非阻塞演算法 (cas)。非阻塞的實現方式是使用迴圈cas的方式來實現。

concurrenthashmapconcurrentlinkedqueue將單獨介紹。本文主要講解幾種阻塞佇列(blockingqueue)的特點。

阻塞佇列(blockingqueue)是乙個支援兩個附加操作的佇列,這兩個操作支援阻塞的插入和移除方法。

支援阻塞的插入方法:當佇列滿時,佇列會阻塞插入元素的執行緒,直到佇列不滿的狀態。

支援阻塞的移除方法:當隊列為空時,獲取元素的執行緒會一致等待佇列變為非空的狀態。

阻塞佇列的應用場景:

阻塞佇列常用於生產者和消費者的場景,生產者是向佇列裡新增元素的執行緒,消費者是

從佇列裡取元素的執行緒。阻塞佇列就是生產者用來存放元素、消費者用來獲取元素的容器。

阻塞佇列內提供了4中 api 處理方式,如下圖所示:

丟擲異常:add(e):當佇列滿時,如果往佇列裡插入元素,會拋異常。

remove():當佇列空時,如果從佇列裡獲取元素,會丟擲異常。

element():當佇列空時,如果從佇列裡獲取元素,會丟擲異常。

返回特殊值:offer(e):當佇列滿時,如果往佇列裡插入元素,會返回 false 表示插入失敗。

poll():當佇列空時,如果從佇列裡獲取元素,會返回 null。

peek():當佇列空時,如果從佇列裡獲取元素,會返回 null。

一直阻塞:put(e):當佇列滿時,如果往佇列裡插入元素,佇列會阻塞當前執行緒,直到佇列可用或者響應中斷退出。

take():當佇列空時,如果從佇列裡獲取元素,佇列會阻塞當前執行緒,直到佇列不為空。

超時退出:offer(time, unit): 當佇列滿時,如果往佇列裡插入元素,佇列會阻塞當前執行緒一段時間,如果超過了指定的時間,當前執行緒就會退出。

poll(time, unit):當佇列空時,如果從佇列裡獲取元素,佇列會阻塞當前執行緒一段時間,如果超過了指定的時間,當前執行緒就會退出。

jdk1.7 提供了7個阻塞佇列:

arrayblockingqueue:乙個由陣列結構組成的有界阻塞佇列。

linkedblockingqueue:乙個由鍊錶結構組成的有界阻塞佇列。

priorityblockingqueue:乙個支援優先順序排序的無界阻塞佇列。

delayqueue:乙個使用優先順序佇列實現的無界阻塞佇列。

synchronousqueue:乙個不儲存元素的阻塞佇列。

linkedtransferqueue:乙個由鍊錶結構組成的無界阻塞佇列。

linkedblockingdeque:乙個由鍊錶結構組成的雙向阻塞佇列。

arrayblockingqueue 是乙個用陣列實現有界阻塞佇列。此佇列按照先進先出(fifo)的原則對元素進行排序。

預設情況下使用非公平鎖進行佇列訪問 (公平鎖會降低吞吐量)。

linkedblockingqueue 是乙個用鍊錶實現有界阻塞佇列。此佇列按照先進先出的原則對元素進行排序。

此佇列的預設和最大長度為integer.max_value。

priorityblockingqueue 是乙個支援優先順序無界阻塞佇列。

預設情況下元素採取自然順序公升序排列。

支援自定義類實現compareto()方法來指定元素排序規則。

同優先順序元素,priorityblockingqueue不能保證其順序。

delayqueue 是乙個支援延時獲取元素的無界阻塞佇列。佇列使用priorityqueue來實現。

佇列中的元素必須實現delayed介面,在建立元素時可以指定多久才能從佇列中獲取當前元素。

只有在延遲期滿時才能從佇列中提取元素。

delayqueue 的應用場景:

快取系統的設計:可以用delayqueue儲存快取元素的有效期,使用乙個執行緒迴圈查詢delayqueue,一旦能從delayqueue中獲取元素時,表示快取有效期到了。

定時任務排程:使用delayqueue儲存當天將會執行的任務和執行時間,一旦從delayqueue中獲取到任務就開始執行。

synchronousqueue 是乙個不儲存元素的阻塞佇列。

每乙個put操作必須等待乙個take操作,否則不能繼續新增元素。

預設情況下執行緒採用非公平性策略訪問佇列。

優勢:synchronousqueue的吞吐量高於linkedblockingqueuearrayblockingqueue

linkedtransferqueue 是乙個由鍊錶結構組成的無界阻塞transferqueue佇列。

linkedblockingdeque 是乙個由鍊錶結構組成的雙向阻塞佇列。

雙向佇列:指可以從佇列的兩端插入和移出元素。

雙向阻塞佇列可以運用在工作竊取模式中。

如果佇列是空的,消費者會一直等待,當生產者新增元素時,消費者是如何知道當前佇列

有元素的呢?

可以使用condition介面的等待通知模式來實現。具體請參考:lock(五) — locksupport 和 condition介面

併發基礎 11 併發 容器

要實現乙個執行緒安全的佇列有兩個方式 一種是使用阻塞演算法,另一種是使用非阻塞演算法。阻塞演算法 使用阻塞演算法的佇列可以用乙個鎖 入隊和出隊同一把鎖 或兩把鎖 入隊和出隊用不同的鎖 來實現。非阻塞的實現方式則可以使用迴圈cas的方式來實現。concurrentlinkedqueue非阻塞執行緒安全...

併發程式設計9 併發容器

解決併發情況下的容器執行緒安全問題 譬如 vector,hashtable,都是給予同步鎖實現的 concurrent包下的類,大部分都是使用系統底層的實現,類似於native public class test09 latch.countdown for thread t arr try catc...

同步容器與併發容器

同步容器 可以簡單地理解為通過synchronized來實現同步的容器,如果有多個執行緒呼叫同步容器的方法,它們將會序列執行。比如vector,hashtable 早起jdk的一部分 及collections.synchronized 等方法返回的容器。可以通過檢視vector,hashtable等...