10分鐘從實(shí)現(xiàn)和使用場(chǎng)景聊聊并發(fā)包下的阻塞隊(duì)列

上篇文章12分鐘從Executor自頂向下徹底搞懂線程池中我們聊到線程池,而線程池中包含阻塞隊(duì)列

這篇文章我們主要聊聊并發(fā)包下的阻塞隊(duì)列

阻塞隊(duì)列

什么是隊(duì)列?

隊(duì)列的實(shí)現(xiàn)可以是數(shù)組、也可以是鏈表,可以實(shí)現(xiàn)先進(jìn)先出的順序隊(duì)列,也可以實(shí)現(xiàn)先進(jìn)后出的棧隊(duì)列

那什么是阻塞隊(duì)列?

在經(jīng)典的生產(chǎn)者/消費(fèi)者模型中,生產(chǎn)者們將生產(chǎn)的元素放入隊(duì)列,而消費(fèi)者們從隊(duì)列獲取元素消費(fèi)

當(dāng)隊(duì)列已滿,我們會(huì)手動(dòng)阻塞生產(chǎn)者,直到消費(fèi)者消費(fèi)再來手動(dòng)喚醒生產(chǎn)者

當(dāng)隊(duì)列為空,我們會(huì)手動(dòng)阻塞消費(fèi)者,直到生產(chǎn)者生產(chǎn)再來手動(dòng)喚醒消費(fèi)者

在這個(gè)過程中由于使用的是普通隊(duì)列,阻塞與喚醒我們需要手動(dòng)操作,保證同步機(jī)制

阻塞隊(duì)列在隊(duì)列的基礎(chǔ)上提供等待/通知功能,用于線程間的通信,避免線程競(jìng)爭(zhēng)死鎖

生產(chǎn)者可以看成往線程池添加任務(wù)的用戶線程,而消費(fèi)者則是線程池中的工作線程

當(dāng)阻塞隊(duì)列為空時(shí)阻塞工作線程獲取任務(wù),當(dāng)阻塞隊(duì)列已滿時(shí)阻塞用戶線程向隊(duì)列中添加任務(wù)(創(chuàng)建非核心線程、拒絕策略)

API

阻塞隊(duì)列提供一下四種添加、刪除元素的API,我們常用阻塞等待/超時(shí)阻塞等待的API

方法名 拋出異常 返回true/false 阻塞等待 超時(shí)阻塞等待
添加 add(Object) offer(Object) put(Object) offer(Object,long,TimeUnit)
刪除 remove() poll() take() poll(long,TimeUnit)
  1. 拋出異常:隊(duì)滿add 拋出異常IllegalStateExceptio ;隊(duì)空remove 拋出異常NoSuchElementException
  2. 返回值: 隊(duì)滿offer返回false,隊(duì)空poll返回null
  3. 阻塞等待: 隊(duì)滿時(shí)put會(huì)阻塞線程 或 隊(duì)空時(shí)take會(huì)阻塞線程
  4. 超時(shí)阻塞等待: 在阻塞等待、返回true/false的基礎(chǔ)上增加超時(shí)等待(等待一定時(shí)間就退出等待)
阻塞隊(duì)列的公平與不公平

什么是阻塞隊(duì)列的公平與不公平?

當(dāng)阻塞隊(duì)列已滿時(shí),如果是公平的,那么阻塞的線程根據(jù)先后順序從阻塞隊(duì)列中獲取元素,不公平則反之

實(shí)際上阻塞隊(duì)列的公平與不公平,要看實(shí)現(xiàn)阻塞隊(duì)列的鎖是否公平

阻塞隊(duì)列一般默認(rèn)使用不公平鎖

ArrayBlockingQueue

從名稱看就可以知道它是數(shù)組實(shí)現(xiàn)的,我們先來看看它有哪些重要字段

 public class ArrayBlockingQueue<E> extends AbstractQueue<E>
         implements BlockingQueue<E>, java.io.Serializable {
 
     //存儲(chǔ)元素的數(shù)組
     final Object[] items;
 
     //記錄元素出隊(duì)的下標(biāo)
     int takeIndex;
 
     //記錄元素入隊(duì)的下標(biāo)
     int putIndex;
 
     //隊(duì)列中元素?cái)?shù)量
     int count;
 
     //使用的鎖
     final ReentrantLock lock;
 
     //出隊(duì)的等待隊(duì)列,作用于消費(fèi)者
     private final Condition notEmpty;
 
     //入隊(duì)的等待隊(duì)列,作用于生產(chǎn)者
     private final Condition notFull;
     
 }

看完關(guān)鍵字段,我們可以知道:ArrayBlockingQueue由數(shù)組實(shí)現(xiàn)、使用并發(fā)包下的可重入鎖、同時(shí)用兩個(gè)等待隊(duì)列作用生產(chǎn)者和消費(fèi)者

為什么出隊(duì)、入隊(duì)要使用兩個(gè)下標(biāo)記錄?

實(shí)際上它是一個(gè)環(huán)形數(shù)組,在初始化后就不改變大小,后續(xù)查看源碼自然能明白它是環(huán)形數(shù)組

在構(gòu)造器中、初始化數(shù)組容量,同時(shí)使用非公平鎖

     public ArrayBlockingQueue(int capacity) {
         this(capacity, false);
     }
 
     public ArrayBlockingQueue(int capacity, boolean fair) {
         if (capacity <= 0)
             throw new IllegalArgumentException();
         this.items = new Object[capacity];
         //鎖是否為公平鎖
         lock = new ReentrantLock(fair);
         notEmpty = lock.newCondition();
         notFull =  lock.newCondition();
     }

ArrayBlockingQueue的公平性是由ReentrantLock來實(shí)現(xiàn)的

我們來看看入隊(duì)方法,入隊(duì)方法都大同小異,我們本文都查看支持超時(shí)、響應(yīng)中斷的方法

     public boolean offer(E e, long timeout, TimeUnit unit)
         throws InterruptedException {
         //檢查空指針
         checkNotNull(e);
         //獲取超時(shí)納秒
         long nanos = unit.toNanos(timeout);
         final ReentrantLock lock = this.lock;
         //加鎖
         lock.lockInterruptibly();
         try {
             //如果隊(duì)列已滿
             while (count == items.length) {
                 //超時(shí)則返回入隊(duì)失敗,否則生產(chǎn)者等待對(duì)應(yīng)時(shí)間
                 if (nanos <= 0)
                     return false;
                 nanos = notFull.awaitNanos(nanos);
             }
             //入隊(duì)
             enqueue(e);
             return true;
         } finally {
             //解鎖
             lock.unlock();
         }
     }

直接使用可重入鎖保證同步,如果隊(duì)列已滿,在此期間判斷是否超時(shí),超時(shí)就返回,未超時(shí)等待;未滿則執(zhí)行入隊(duì)方法

     private void enqueue(E x) {
         //隊(duì)列數(shù)組
         final Object[] items = this.items;
         //往入隊(duì)下標(biāo)添加值
         items[putIndex] = x;
         //自增入隊(duì)下標(biāo) 如果已滿則定位到0 成環(huán)
         if (++putIndex == items.length)
             putIndex = 0;
         //統(tǒng)計(jì)數(shù)量增加
         count++;
         //喚醒消費(fèi)者
         notEmpty.signal();
     }

在入隊(duì)中,主要是添加元素、修改下次添加的下標(biāo)、統(tǒng)計(jì)隊(duì)列中的元素和喚醒消費(fèi)者,到這以及可以說明它的實(shí)現(xiàn)是環(huán)形數(shù)組

ArrayBlockingQueue由環(huán)形數(shù)組實(shí)現(xiàn)的阻塞隊(duì)列,固定容量不支持動(dòng)態(tài)擴(kuò)容,使用非公平的ReertrantLock保證入隊(duì)、出隊(duì)操作的原子性,使用兩個(gè)等待隊(duì)列存儲(chǔ)等待的生產(chǎn)者、消費(fèi)者,適用于在并發(fā)量不大的場(chǎng)景

LinkedBlockingQueue

LinkedBlockingQueue從名稱上來看,就是使用鏈表實(shí)現(xiàn)的,我們來看看它的關(guān)鍵字段

 public class LinkedBlockingQueue<E> extends AbstractQueue<E>
         implements BlockingQueue<E>, java.io.Serializable {
     //節(jié)點(diǎn)
     static class Node<E> {
         //存儲(chǔ)元素
         E item;
 
         //下一個(gè)節(jié)點(diǎn)
         Node<E> next;
         
         //...
     }
 
     //容量上限
     private final int capacity;
 
     //隊(duì)列元素?cái)?shù)量
     private final AtomicInteger count = new AtomicInteger();
 
     //頭節(jié)點(diǎn)
     transient Node<E> head;
 
     //尾節(jié)點(diǎn)
     private transient Node<E> last;
 
     //出隊(duì)的鎖
     private final ReentrantLock takeLock = new ReentrantLock();
 
     //出隊(duì)的等待隊(duì)列
     private final Condition notEmpty = takeLock.newCondition();
 
     //入隊(duì)的鎖
     private final ReentrantLock putLock = new ReentrantLock();
 
     //入隊(duì)的等待隊(duì)列
     private final Condition notFull = putLock.newCondition();
 }

從字段中,我們可以知道它使用單向鏈表的節(jié)點(diǎn)、且用首尾節(jié)點(diǎn)記錄隊(duì)列的頭尾,并且它使用兩把鎖、兩個(gè)等待隊(duì)列作用于隊(duì)頭、尾,與ArrayBlockingQueue相比能夠增加并發(fā)性能

有個(gè)奇怪的地方:都使用鎖了,為什么記錄元素?cái)?shù)量count卻使用原子類呢?

這是由于兩把鎖,作用于入隊(duì)與出隊(duì)的操作,入隊(duì)與出隊(duì)也可能并發(fā)執(zhí)行,同時(shí)修改count,因此要使用原子類保證修改數(shù)量的原子性

在初始化時(shí)需要設(shè)置容量大小,否則會(huì)設(shè)置成無界的阻塞隊(duì)列(容量是int的最大值)

當(dāng)消費(fèi)速度小于生產(chǎn)速度時(shí),阻塞隊(duì)列中會(huì)堆積任務(wù),進(jìn)而導(dǎo)致容易發(fā)生OOM

     public LinkedBlockingQueue() {
         this(Integer.MAX_VALUE);
     }
 
     public LinkedBlockingQueue(int capacity) {
         if (capacity <= 0) throw new IllegalArgumentException();
         this.capacity = capacity;
         last = head = new Node<E>(null);
     }

來看看入隊(duì)操作

     public boolean offer(E e, long timeout, TimeUnit unit)
         throws InterruptedException {
 
         if (e == null) throw new NullPointerException();
         long nanos = unit.toNanos(timeout);
         int c = -1;
         final ReentrantLock putLock = this.putLock;
         final AtomicInteger count = this.count;
         //加鎖
         putLock.lockInterruptibly();
         try {
             //隊(duì)列已滿,超時(shí)返回,不超時(shí)等待
             while (count.get() == capacity) {
                 if (nanos <= 0)
                     return false;
                 nanos = notFull.awaitNanos(nanos);
             }
             //入隊(duì)
             enqueue(new Node<E>(e));
             // 先獲取再自增 c中存儲(chǔ)的是舊值
             c = count.getAndIncrement();
             //如果數(shù)量沒滿 喚醒生產(chǎn)者
             if (c + 1 < capacity)
                 notFull.signal();
         } finally {
             //解鎖
             putLock.unlock();
         }
         //如果舊值為0 說明該入隊(duì)操作前是空隊(duì)列,喚醒消費(fèi)者來消費(fèi)
         if (c == 0)
             signalNotEmpty();
         return true;
     }

入隊(duì)操作類似,只不過在此期間如果數(shù)量沒滿喚醒生產(chǎn)者生產(chǎn),隊(duì)列為空喚醒消費(fèi)者來消費(fèi),從而增加并發(fā)性能

入隊(duì)只是改變指向關(guān)系

     //添加節(jié)點(diǎn)到末尾
     private void enqueue(Node<E> node) {
         last = last.next = node;
     }

喚醒消費(fèi)者前要先獲取鎖

     private void signalNotEmpty() {
         final ReentrantLock takeLock = this.takeLock;
         takeLock.lock();
         try {
             notEmpty.signal();
         } finally {
             takeLock.unlock();
         }
     }

出隊(duì)操作也類似

     public E poll(long timeout, TimeUnit unit) throws InterruptedException {
         E x = null;
         int c = -1;
         long nanos = unit.toNanos(timeout);
         final AtomicInteger count = this.count;
         final ReentrantLock takeLock = this.takeLock;
         takeLock.lockInterruptibly();
         try {
             // 隊(duì)列為空 超時(shí)返回空,否則等待
             while (count.get() == 0) {
                 if (nanos <= 0)
                     return null;
                 nanos = notEmpty.awaitNanos(nanos);
             }
             //出隊(duì)
             x = dequeue();
             c = count.getAndDecrement();
             //隊(duì)列中除了當(dāng)前線程獲取的任務(wù)外還有任務(wù)就去喚醒消費(fèi)者消費(fèi)
             if (c > 1)
                 notEmpty.signal();
         } finally {
             takeLock.unlock();
         }
         //原來隊(duì)列已滿就去喚醒生產(chǎn)者 生產(chǎn)
         if (c == capacity)
             signalNotFull();
         return x;
     }

LinkedBlockingQueueArrayBlockingQueue的出隊(duì)、入隊(duì)實(shí)現(xiàn)類似

只不過LinkedBlockingQueue入隊(duì)、出隊(duì)獲取/釋放的鎖不同,并且在此過程中不同情況回去喚醒其他的生產(chǎn)者、消費(fèi)者從而進(jìn)一步提升并發(fā)性能

LinkedBlockingQueue 由單向鏈表實(shí)現(xiàn)的阻塞隊(duì)列,記錄首尾節(jié)點(diǎn);默認(rèn)是無界、非公平的阻塞隊(duì)列(初始化時(shí)要設(shè)置容量否則可能OOM),使用兩把鎖、兩個(gè)等待隊(duì)列,分別操作入隊(duì)、出隊(duì)的生產(chǎn)者、消費(fèi)者,在入隊(duì)、出隊(duì)操作期間不同情況還會(huì)去喚醒生產(chǎn)者、消費(fèi)者,從而進(jìn)一步提升并發(fā)性能,適用于并發(fā)量大的場(chǎng)景

LinkedBlockingDeque

LinkedBlockingDeque實(shí)現(xiàn)與LinkedBlockQueue類似,在LinkedBlockQueue的基礎(chǔ)上支持從隊(duì)頭、隊(duì)尾進(jìn)行添加、刪除的操作

它是一個(gè)雙向鏈表,帶有一系列First、Last的方法,比如:offerLast、pollFirst

由于LinkedBlockingDeque雙向,常用其來實(shí)現(xiàn)工作竊取算法,從而減少線程的競(jìng)爭(zhēng)

什么是工作竊取算法?

比如多線程處理多個(gè)阻塞隊(duì)列的任務(wù)(一一對(duì)應(yīng)),每個(gè)線程從隊(duì)頭獲取任務(wù)處理,當(dāng)A線程處理完它負(fù)責(zé)的阻塞隊(duì)列所有任務(wù)時(shí),它再?gòu)年?duì)尾竊取其他阻塞隊(duì)列的任務(wù),這樣就不會(huì)發(fā)生競(jìng)爭(zhēng),除非隊(duì)列中只剩一個(gè)任務(wù),才會(huì)發(fā)生競(jìng)爭(zhēng)

ForkJoin框架就使用其來充當(dāng)阻塞隊(duì)列,我們后文再聊這個(gè)框架

PriorityBlockingQueue

PriorityBlockingQueue是優(yōu)先級(jí)排序的無界阻塞隊(duì)列,阻塞隊(duì)列按照優(yōu)先級(jí)進(jìn)行排序

使用堆排序,具體排序算法由ComparableComparator實(shí)現(xiàn)比較規(guī)則

  1. 默認(rèn):泛型中的對(duì)象需要實(shí)現(xiàn)Comparable比較規(guī)則 ,根據(jù)compareTo方法規(guī)則排序
  2. 構(gòu)造器中指定比較器Comparator 根據(jù)比較器規(guī)則排序
     @Test
     public void testPriorityBlockingQeque() {
         //默認(rèn)使用Integer實(shí)現(xiàn)Comparable的升序
         PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(6);
         queue.offer(99);
         queue.offer(1099);
         queue.offer(299);
         queue.offer(992);
         queue.offer(99288);
         queue.offer(995);
         //99 299 992 995 1099 99288
         while (!queue.isEmpty()){
             System.out.print(" "+queue.poll());
         }
 
         System.out.println();
         //指定Comparator 降序
         queue = new PriorityBlockingQueue<>(6, (o1, o2) -> o2-o1);
         queue.offer(99);
         queue.offer(1099);
         queue.offer(299);
         queue.offer(992);
         queue.offer(99288);
         queue.offer(995);
         //99288 1099 995 992 299 99
         while (!queue.isEmpty()){
             System.out.print(" "+queue.poll());
         }
     }

適用于需要根據(jù)優(yōu)先級(jí)排序處理的場(chǎng)景

DelayQueue

Delay是一個(gè)延時(shí)獲取元素的無界阻塞隊(duì)列, 延時(shí)最長(zhǎng)排在隊(duì)尾

Delay隊(duì)列元素實(shí)現(xiàn)Delayed接口通過getDelay獲取延時(shí)時(shí)間

 public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
     implements BlockingQueue<E> {
 }
 
 public interface Delayed extends Comparable<Delayed> {
     long getDelay(TimeUnit unit);
 }

DelayQueue應(yīng)用場(chǎng)景

  1. 緩存系統(tǒng)的設(shè)計(jì):DelayQueue存放緩存有效期,當(dāng)可以獲取到元素時(shí),說明緩存過期
  2. 定時(shí)任務(wù)調(diào)度: 將定時(shí)任務(wù)的時(shí)間設(shè)置為延時(shí)時(shí)間,一旦可以獲取到任務(wù)就開始執(zhí)行

以定時(shí)線程池ScheduledThreadPoolExecutor的定時(shí)任務(wù)ScheduledFutureTask為例,它實(shí)現(xiàn)Delayed獲取延遲執(zhí)行的時(shí)間

  1. 創(chuàng)建對(duì)象時(shí),初始化數(shù)據(jù)

             ScheduledFutureTask(Runnable r, V result, long ns, long period) {
                 super(r, result);
                 //time記錄當(dāng)前對(duì)象延遲到什么時(shí)候可以使用,單位是納秒
                 this.time = ns;
                 this.period = period;
                 //sequenceNumber記錄元素在隊(duì)列中先后順序  sequencer原子自增
                 //AtomicLong sequencer = new AtomicLong();
                 this.sequenceNumber = sequencer.getAndIncrement();
             }
    
  2. 實(shí)現(xiàn)Delayed接口的getDelay方法

     public long getDelay(TimeUnit unit) {
         return unit.convert(time - now(), NANOSECONDS);
     }
    
  3. Delay接口繼承了Comparable接口,目的是要實(shí)現(xiàn)compareTo方法來繼續(xù)排序

             public int compareTo(Delayed other) {
                 if (other == this) // compare zero if same object
                     return 0;
                 if (other instanceof ScheduledFutureTask) {
                     ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
                     long diff = time - x.time;
                     if (diff < 0)
                         return -1;
                     else if (diff > 0)
                         return 1;
                     else if (sequenceNumber < x.sequenceNumber)
                         return -1;
                     else
                         return 1;
                 }
                 long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
                 return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
             }
    

SynchronousQueue

SynchronousQueue是一個(gè)默認(rèn)下支持非公平不存儲(chǔ)元素的阻塞隊(duì)列

每個(gè)put操作要等待一個(gè)take操作,否則不能繼續(xù)添加元素會(huì)阻塞

使用公平鎖

     @Test
     public void testSynchronousQueue() throws InterruptedException {
         final SynchronousQueue<Integer> queue = new SynchronousQueue(true);
         new Thread(() -> {
             try {
                 queue.put(1);
                 queue.put(2);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }, "put12線程").start();
 
         new Thread(() -> {
             try {
                 queue.put(3);
                 queue.put(4);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }, "put34線程").start();
 
         TimeUnit.SECONDS.sleep(1);
         System.out.println(Thread.currentThread().getName() + "拿出" + queue.take());
         TimeUnit.SECONDS.sleep(1);
         System.out.println(Thread.currentThread().getName() + "拿出" + queue.take());
         TimeUnit.SECONDS.sleep(1);
         System.out.println(Thread.currentThread().getName() + "拿出" + queue.take());
         TimeUnit.SECONDS.sleep(1);
         System.out.println(Thread.currentThread().getName() + "拿出" + queue.take());
     }
 //結(jié)果 因?yàn)槭褂霉芥i 1在2前,3在4前
 //main拿出1
 //main拿出3
 //main拿出2
 //main拿出4

SynchronousQueue隊(duì)列本身不存儲(chǔ)元素,負(fù)責(zé)把生產(chǎn)者的數(shù)據(jù)傳遞給消費(fèi)者,適合傳遞性的場(chǎng)景

在該場(chǎng)景下吞吐量會(huì)比ArrayBlockingQueue,LinkedBlockingQueue高

LinkedTransferQueue

LinkedTransferQueue是一個(gè)鏈表組成的無界阻塞隊(duì)列,擁有transfer()tryTransfer()方法

transfer()

如果有消費(fèi)者在等待接收元素,transfer(e)會(huì)把元素e傳輸給消費(fèi)者

如果沒有消費(fèi)者在等待接收元素,transfer(e)會(huì)將元素e存放在隊(duì)尾,直到有消費(fèi)者獲取了才返回

     @Test
     public void testTransfer() throws InterruptedException {
         LinkedTransferQueue queue = new LinkedTransferQueue();
         new Thread(()->{
             try {
                 //阻塞直到被獲取
                 queue.transfer(1);
                 //生產(chǎn)者放入的1被取走了
                 System.out.println(Thread.currentThread().getName()+"放入的1被取走了");
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         },"生產(chǎn)者").start();
 
         TimeUnit.SECONDS.sleep(3);
         //main取出隊(duì)列中的元素
         System.out.println(Thread.currentThread().getName()+"取出隊(duì)列中的元素");
         queue.poll();
     }

tryTransfer()無論消費(fèi)者是否消費(fèi)都直接返回

     @Test
     public void testTryTransfer() throws InterruptedException {
         LinkedTransferQueue<Integer> queue = new LinkedTransferQueue<>();
         //false
         System.out.println(queue.tryTransfer(1));
         //null
         System.out.println(queue.poll());
 
         new Thread(()->{
             try {
                 //消費(fèi)者取出2
                 System.out.println(Thread.currentThread().getName()+"取出"+queue.poll(2, TimeUnit.SECONDS));
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         },"消費(fèi)者").start();
         TimeUnit.SECONDS.sleep(1);
         //true
         System.out.println(queue.tryTransfer(2));
     }

tryTransfer(long,TimeUnit) 在超時(shí)時(shí)間內(nèi)消費(fèi)者消費(fèi)元素返回true,反之返回false

總結(jié)

ArrayBlockingQueue由環(huán)形數(shù)組實(shí)現(xiàn),固定容量無法擴(kuò)容,使用非公平的可重入鎖鎖、兩個(gè)等待隊(duì)列操作入隊(duì)、出隊(duì)操作,適合并發(fā)小的場(chǎng)景

LinkedBlockingQueue由單向鏈表實(shí)現(xiàn),默認(rèn)無界,使用兩個(gè)可重入鎖、兩個(gè)等待隊(duì)列進(jìn)行入隊(duì)、出隊(duì)操作,并在此期間可能喚醒生產(chǎn)者或消費(fèi)者線程,以此提高并發(fā)性能

LinkedBlockingDeque由雙向鏈表實(shí)現(xiàn),在LinkedBlockingQueue的基礎(chǔ)上,能夠在隊(duì)頭、隊(duì)尾都進(jìn)行添加、刪除操作,適用工作竊取算法1

PriorityBlockingQueue由堆排序?qū)崿F(xiàn)的優(yōu)先級(jí)隊(duì)列,具體排序算法由Comparable、Comparator來實(shí)現(xiàn),適用于需要根據(jù)優(yōu)先級(jí)排序處理任務(wù)的場(chǎng)景

DelayQueue 是一個(gè)延時(shí)隊(duì)列,隊(duì)列中存儲(chǔ)的元素需要實(shí)現(xiàn)Delayed接口來獲取延時(shí)時(shí)間,適用于緩存失效、定時(shí)任務(wù)的場(chǎng)景

SynchronousQueue不存儲(chǔ)元素,只將生產(chǎn)者生產(chǎn)的元素傳遞給消費(fèi)者, 適用于傳遞性的場(chǎng)景,比如不同線程間傳遞數(shù)據(jù)

LinkedTransgerQueue是傳輸形的阻塞隊(duì)列,適用于單個(gè)元素傳遞的場(chǎng)景

在使用無界的阻塞隊(duì)列時(shí),需要設(shè)置容量,避免存儲(chǔ)任務(wù)太多導(dǎo)致OOM

最后(不要白嫖,一鍵三連求求拉~)

本篇文章被收入專欄 由點(diǎn)到線,由線到面,深入淺出構(gòu)建Java并發(fā)編程知識(shí)體系,感興趣的同學(xué)可以持續(xù)關(guān)注喔

本篇文章筆記以及案例被收入 gitee-StudyJava、 github-StudyJava 感興趣的同學(xué)可以stat下持續(xù)關(guān)注喔~

案例地址:

Gitee-JavaConcurrentProgramming/src/main/java/E_BlockQueue

Github-JavaConcurrentProgramming/src/main/java/E_BlockQueue

有什么問題可以在評(píng)論區(qū)交流,如果覺得菜菜寫的不錯(cuò),可以點(diǎn)贊、關(guān)注、收藏支持一下~

關(guān)注菜菜,分享更多干貨,公眾號(hào):菜菜的后端私房菜

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容