Java從零開始學 - 第25天:掌握JUC中的阻塞隊列

2021-02-19 Java研究所

環境:jdk1.8。

本文內容

掌握Queue、BlockingQueue接口中常用的方法

介紹6中阻塞隊列,及相關場景示例

重點掌握4種常用的阻塞隊列

Queue接口

隊列是一種先進先出(FIFO)的數據結構,java中用Queue接口來表示隊列。

Queue接口中定義了6個方法:

public interface Queue<E> extends Collection<E> {
    boolean add(e);
    boolean offer(E e);
    E remove();
    E poll();
    E element();
    E peek();
}

每個Queue方法都有兩種形式:

(1)如果操作失敗則拋出異常,

(2)如果操作失敗,則返回特殊值(null或false,具體取決於操作),接口的常規結構如下表所示。

操作類型拋出異常返回特殊值插入add(e)offer(e)移除remove()poll()檢查element()peek()

Queue從Collection繼承的add方法插入一個元素,除非它違反了隊列的容量限制,在這種情況下它會拋出IllegalStateException;offer方法與add不同之處僅在於它通過返回false來表示插入元素失敗。

remove和poll方法都移除並返回隊列的頭部,確切地移除哪個元素是由具體的實現來決定的,僅當隊列為空時,remove和poll方法的行為才有所不同,在這些情況下,remove拋出NoSuchElementException,而poll返回null。

element和peek方法返回隊列頭部的元素,但不移除,它們之間的差異與remove和poll的方式完全相同,如果隊列為空,則element拋出NoSuchElementException,而peek返回null。

隊列一般不要插入空元素。

BlockingQueue接口

BlockingQueue位於juc中,熟稱阻塞隊列, 阻塞隊列首先它是一個隊列,繼承Queue接口,是隊列就會遵循先進先出(FIFO)的原則,又因為它是阻塞的,故與普通的隊列有兩點區別:

當一個線程向隊列裡面添加數據時,如果隊列是滿的,那麼將阻塞該線程,暫停添加數據

當一個線程從隊列裡面取出數據時,如果隊列是空的,那麼將阻塞該線程,暫停取出數據

BlockingQueue相關方法:

操作類型拋出異常返回特殊值一直阻塞超時退出插入add(e)offer(e)put(e)offer(e,timeuout,unit)移除remove()poll()take()poll(timeout,unit)檢查element()peek()不支持不支持

重點,再來解釋一下,加深印象:

3個可能會有異常的方法,add、remove、element;這3個方法不會阻塞(是說隊列滿或者空的情況下是否會阻塞);隊列滿的情況下,add拋出異常;隊列為空情況下,remove、element拋出異常

offer、poll、peek 也不會阻塞(是說隊列滿或者空的情況下是否會阻塞);隊列滿的情況下,offer返回false;隊列為空的情況下,pool、peek返回null

隊列滿的情況下,調用put方法會導致當前線程阻塞

隊列為空的情況下,調用take方法會導致當前線程阻塞

offer(e,timeuout,unit),超時之前,插入成功返回true,否者返回false

poll(timeout,unit),超時之前,獲取到頭部元素並將其移除,返回true,否者返回false

以上一些方法希望大家都記住,方便以後使用

BlockingQueue常見的實現類

看一下相關類圖

ArrayBlockingQueue

基於數組的阻塞隊列實現,其內部維護一個定長的數組,用於存儲隊列元素。線程阻塞的實現是通過ReentrantLock來完成的,數據的插入與取出共用同一個鎖,因此ArrayBlockingQueue並不能實現生產、消費同時進行。而且在創建ArrayBlockingQueue時,我們還可以控制對象的內部鎖是否採用公平鎖,默認採用非公平鎖。

LinkedBlockingQueue

基於單向鍊表的阻塞隊列實現,在初始化LinkedBlockingQueue的時候可以指定大小,也可以不指定,默認類似一個無限大小的容量(Integer.MAX_VALUE),不指隊列容量大小也是會有風險的,一旦數據生產速度大於消費速度,系統內存將有可能被消耗殆盡,因此要謹慎操作。另外LinkedBlockingQueue中用於阻塞生產者、消費者的鎖是兩個(鎖分離),因此生產與消費是可以同時進行的。

PriorityBlockingQueue

一個支持優先級排序的無界阻塞隊列,進入隊列的元素會按照優先級進行排序

SynchronousQueue

同步阻塞隊列,SynchronousQueue沒有容量,與其他BlockingQueue不同,SynchronousQueue是一個不存儲元素的BlockingQueue,每一個put操作必須要等待一個take操作,否則不能繼續添加元素,反之亦然

DelayQueue

DelayQueue是一個支持延時獲取元素的無界阻塞隊列,裡面的元素全部都是「可延期」的元素,列頭的元素是最先「到期」的元素,如果隊列裡面沒有元素到期,是不能從列頭獲取元素的,哪怕有元素也不行,也就是說只有在延遲期到時才能夠從隊列中取元素

LinkedTransferQueue

LinkedTransferQueue是基於鍊表的FIFO無界阻塞隊列,它出現在JDK7中,Doug Lea 大神說LinkedTransferQueue是一個聰明的隊列,它是ConcurrentLinkedQueue、SynchronousQueue(公平模式下)、無界的LinkedBlockingQueues等的超集,LinkedTransferQueue包含了ConcurrentLinkedQueue、SynchronousQueue、LinkedBlockingQueues三種隊列的功能

下面我們來介紹每種阻塞隊列的使用。

ArrayBlockingQueue

有界阻塞隊列,內部使用數組存儲元素,有2個常用構造方法:

//capacity表示容量大小,默認內部採用非公平鎖
public ArrayBlockingQueue(int capacity)
//capacity:容量大小,fair:內部是否是使用公平鎖
public ArrayBlockingQueue(int capacity, boolean fair)

需求:業務系統中有很多地方需要推送通知,由於需要推送的數據太多,我們將需要推送的信息先丟到阻塞隊列中,然後開一個線程進行處理真實發送,代碼如下:

package com.itsoku.chat25;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import sun.text.normalizer.NormalizerBase;

import java.util.Calendar;
import java.util.concurrent.*;

/**
 * 跟著阿里p7學並發,微信公眾號:javacode2018
 */
public class Demo1 {
    //推送隊列
    static ArrayBlockingQueue<String> pushQueue = new ArrayBlockingQueue<String>(10000);

    static {
        //啟動一個線程做真實推送
        new Thread(() -> {
            while (true) {
                String msg;
                try {
                    long starTime = System.currentTimeMillis();
                    //獲取一條推送消息,此方法會進行阻塞,直到返回結果
                    msg = pushQueue.take();
                    long endTime = System.currentTimeMillis();
                    //模擬推送耗時
                    TimeUnit.MILLISECONDS.sleep(500);

                    System.out.println(String.format("[%s,%s,take耗時:%s],%s,發送消息:%s", starTime, endTime, (endTime - starTime), Thread.currentThread().getName(), msg));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    //推送消息,需要發送推送消息的調用該方法,會將推送信息先加入推送隊列
    public static void pushMsg(String msg) throws InterruptedException {
        pushQueue.put(msg);
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            String msg = "一起來學java高並發,第" + i + "天";
            //模擬耗時
            TimeUnit.SECONDS.sleep(i);
            Demo1.pushMsg(msg);
        }
    }
}

輸出:

[1565595629206,1565595630207,take耗時:1001],Thread-0,發送消息:一起來學java高並發,第1天
[1565595630208,1565595632208,take耗時:2000],Thread-0,發送消息:一起來學java高並發,第2天
[1565595632208,1565595635208,take耗時:3000],Thread-0,發送消息:一起來學java高並發,第3天
[1565595635208,1565595639209,take耗時:4001],Thread-0,發送消息:一起來學java高並發,第4天
[1565595639209,1565595644209,take耗時:5000],Thread-0,發送消息:一起來學java高並發,第5天

代碼中我們使用了有界隊列ArrayBlockingQueue,創建ArrayBlockingQueue時候需要制定容量大小,調用pushQueue.put將推送信息放入隊列中,如果隊列已滿,此方法會阻塞。代碼中在靜態塊中啟動了一個線程,調用pushQueue.take();從隊列中獲取待推送的信息進行推送處理。

注意:ArrayBlockingQueue如果隊列容量設置的太小,消費者發送的太快,消費者消費的太慢的情況下,會導致隊列空間滿,調用put方法會導致發送者線程阻塞,所以注意設置合理的大小,協調好消費者的速度。

LinkedBlockingQueue

內部使用單向鍊表實現的阻塞隊列,3個構造方法:

//默認構造方法,容量大小為Integer.MAX_VALUE
public LinkedBlockingQueue();
//創建指定容量大小的LinkedBlockingQueue
public LinkedBlockingQueue(int capacity);
//容量為Integer.MAX_VALUE,並將傳入的集合丟入隊列中
public LinkedBlockingQueue(Collection<? extends E> c);

LinkedBlockingQueue的用法和ArrayBlockingQueue類似,建議使用的時候指定容量,如果不指定容量,插入的太快,移除的太慢,可能會產生OOM。

PriorityBlockingQueue

無界的優先級阻塞隊列,內部使用數組存儲數據,達到容量時,會自動進行擴容,放入的元素會按照優先級進行排序,4個構造方法:

//默認構造方法,默認初始化容量是11
public PriorityBlockingQueue();
//指定隊列的初始化容量
public PriorityBlockingQueue(int initialCapacity);
//指定隊列的初始化容量和放入元素的比較器
public PriorityBlockingQueue(int initialCapacity,Comparator<? super E> comparator);
//傳入集合放入來初始化隊列,傳入的集合可以實現SortedSet接口或者PriorityQueue接口進行排序,如果沒有實現這2個接口,按正常順序放入隊列
public PriorityBlockingQueue(Collection<? extends E> c);

優先級隊列放入元素的時候,會進行排序,所以我們需要指定排序規則,有2種方式:

創建PriorityBlockingQueue指定比較器Comparator

放入的元素需要實現Comparable接口

上面2種方式必須選一個,如果2個都有,則走第一個規則排序。

需求:還是上面的推送業務,目前推送是按照放入的先後順序進行發送的,比如有些公告比較緊急,優先級比較高,需要快點發送,怎麼搞?此時PriorityBlockingQueue就派上用場了,代碼如下:

package com.itsoku.chat25;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * 跟著阿里p7學並發,微信公眾號:javacode2018
 */
public class Demo2 {

    //推送信息封裝
    static class Msg implements Comparable<Msg> {
        //優先級,越小優先級越高
        private int priority;
        //推送的信息
        private String msg;

        public Msg(int priority, String msg) {
            this.priority = priority;
            this.msg = msg;
        }

        @Override
        public int compareTo(Msg o) {
            return Integer.compare(this.priority, o.priority);
        }

        @Override
        public String toString() {
            return "Msg{" +
                    "priority=" + priority +
                    ", msg='" + msg + '\'' +
                    '}';
        }
    }

    //推送隊列
    static PriorityBlockingQueue<Msg> pushQueue = new PriorityBlockingQueue<Msg>();

    static {
        //啟動一個線程做真實推送
        new Thread(() -> {
            while (true) {
                Msg msg;
                try {
                    long starTime = System.currentTimeMillis();
                    //獲取一條推送消息,此方法會進行阻塞,直到返回結果
                    msg = pushQueue.take();
                    //模擬推送耗時
                    TimeUnit.MILLISECONDS.sleep(100);
                    long endTime = System.currentTimeMillis();
                    System.out.println(String.format("[%s,%s,take耗時:%s],%s,發送消息:%s", starTime, endTime, (endTime - starTime), Thread.currentThread().getName(), msg));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    //推送消息,需要發送推送消息的調用該方法,會將推送信息先加入推送隊列
    public static void pushMsg(int priority, String msg) throws InterruptedException {
        pushQueue.put(new Msg(priority, msg));
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 5; i >= 1; i--) {
            String msg = "一起來學java高並發,第" + i + "天";
            Demo2.pushMsg(i, msg);
        }
    }
}

輸出:

[1565598857028,1565598857129,take耗時:101],Thread-0,發送消息:Msg{priority=1, msg='一起來學java高並發,第1天'}
[1565598857162,1565598857263,take耗時:101],Thread-0,發送消息:Msg{priority=2, msg='一起來學java高並發,第2天'}
[1565598857263,1565598857363,take耗時:100],Thread-0,發送消息:Msg{priority=3, msg='一起來學java高並發,第3天'}
[1565598857363,1565598857463,take耗時:100],Thread-0,發送消息:Msg{priority=4, msg='一起來學java高並發,第4天'}
[1565598857463,1565598857563,take耗時:100],Thread-0,發送消息:Msg{priority=5, msg='一起來學java高並發,第5天'}

main中放入了5條推送信息,i作為消息的優先級按倒敘放入的,最終輸出結果中按照優先級由小到大輸出。注意Msg實現了Comparable接口,具有了比較功能。

SynchronousQueue

同步阻塞隊列,SynchronousQueue沒有容量,與其他BlockingQueue不同,SynchronousQueue是一個不存儲元素的BlockingQueue,每一個put操作必須要等待一個take操作,否則不能繼續添加元素,反之亦然。SynchronousQueue 在現實中用的不多,線程池中有用到過,Executors.newCachedThreadPool()實現中用到了這個隊列,當有任務丟入線程池的時候,如果已創建的工作線程都在忙於處理任務,則會新建一個線程來處理丟入隊列的任務。

來個示例代碼:

package com.itsoku.chat25;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/**
 * 跟著阿里p7學並發,微信公眾號:javacode2018
 */
public class Demo3 {

    static SynchronousQueue<String> queue = new SynchronousQueue<>();

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            try {
                long starTime = System.currentTimeMillis();
                queue.put("java高並發系列,路人甲Java!");
                long endTime = System.currentTimeMillis();
                System.out.println(String.format("[%s,%s,take耗時:%s],%s", starTime, endTime, (endTime - starTime), Thread.currentThread().getName()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //休眠5秒之後,從隊列中take一個元素
        TimeUnit.SECONDS.sleep(5);
        System.out.println(System.currentTimeMillis() + "調用take獲取並移除元素," + queue.take());
    }
}

輸出:

1565600421645調用take獲取並移除元素,java高並發系列,路人甲Java!
[1565600416645,1565600421645,take耗時:5000],Thread-0

main方法中啟動了一個線程,調用queue.put方法向隊列中丟入一條數據,調用的時候產生了阻塞,從輸出結果中可以看出,直到take方法被調用時,put方法才從阻塞狀態恢復正常。

DelayQueue

DelayQueue是一個支持延時獲取元素的無界阻塞隊列,裡面的元素全部都是「可延期」的元素,列頭的元素是最先「到期」的元素,如果隊列裡面沒有元素到期,是不能從列頭獲取元素的,哪怕有元素也不行,也就是說只有在延遲期到時才能夠從隊列中取元素。

需求:還是推送的業務,有時候我們希望早上9點或者其他指定的時間進行推送,如何實現呢?此時DelayQueue就派上用場了。

我們先看一下DelayQueue類的聲明:

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E>

元素E需要實現接口Delayed,我們看一下這個接口的代碼:

public interface Delayed extends Comparable<Delayed> {
    long getDelay(TimeUnit unit);
}

Delayed繼承了Comparable接口,這個接口是用來做比較用的,DelayQueue內部使用PriorityQueue來存儲數據的,PriorityQueue是一個優先級隊列,丟入的數據會進行排序,排序方法調用的是Comparable接口中的方法。下面主要說一下Delayed接口中的getDelay方法:此方法在給定的時間單位內返回與此對象關聯的剩餘延遲時間。

對推送我們再做一下處理,讓其支持定時發送(定時在將來某個時間也可以說是延遲發送),代碼如下:

package com.itsoku.chat25;

import java.util.Calendar;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * 跟著阿里p7學並發,微信公眾號:javacode2018
 */
public class Demo4 {

    //推送信息封裝
    static class Msg implements Delayed {
        //優先級,越小優先級越高
        private int priority;
        //推送的信息
        private String msg;
        //定時發送時間,毫秒格式
        private long sendTimeMs;

        public Msg(int priority, String msg, long sendTimeMs) {
            this.priority = priority;
            this.msg = msg;
            this.sendTimeMs = sendTimeMs;
        }

        @Override
        public String toString() {
            return "Msg{" +
                    "priority=" + priority +
                    ", msg='" + msg + '\'' +
                    ", sendTimeMs=" + sendTimeMs +
                    '}';
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(this.sendTimeMs - Calendar.getInstance().getTimeInMillis(), TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            if (o instanceof Msg) {
                Msg c2 = (Msg) o;
                return Integer.compare(this.priority, c2.priority);
            }
            return 0;
        }
    }

    //推送隊列
    static DelayQueue<Msg> pushQueue = new DelayQueue<Msg>();

    static {
        //啟動一個線程做真實推送
        new Thread(() -> {
            while (true) {
                Msg msg;
                try {
                    //獲取一條推送消息,此方法會進行阻塞,直到返回結果
                    msg = pushQueue.take();
                    //此處可以做真實推送
                    long endTime = System.currentTimeMillis();
                    System.out.println(String.format("定時發送時間:%s,實際發送時間:%s,發送消息:%s", msg.sendTimeMs, endTime, msg));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    //推送消息,需要發送推送消息的調用該方法,會將推送信息先加入推送隊列
    public static void pushMsg(int priority, String msg, long sendTimeMs) throws InterruptedException {
        pushQueue.put(new Msg(priority, msg, sendTimeMs));
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 5; i >= 1; i--) {
            String msg = "一起來學java高並發,第" + i + "天";
            Demo4.pushMsg(i, msg, Calendar.getInstance().getTimeInMillis() + i * 2000);
        }
    }
}

輸出:

定時發送時間:1565603357198,實際發送時間:1565603357198,發送消息:Msg{priority=1, msg='一起來學java高並發,第1天', sendTimeMs=1565603357198}
定時發送時間:1565603359198,實際發送時間:1565603359198,發送消息:Msg{priority=2, msg='一起來學java高並發,第2天', sendTimeMs=1565603359198}
定時發送時間:1565603361198,實際發送時間:1565603361199,發送消息:Msg{priority=3, msg='一起來學java高並發,第3天', sendTimeMs=1565603361198}
定時發送時間:1565603363198,實際發送時間:1565603363199,發送消息:Msg{priority=4, msg='一起來學java高並發,第4天', sendTimeMs=1565603363198}
定時發送時間:1565603365182,實際發送時間:1565603365183,發送消息:Msg{priority=5, msg='一起來學java高並發,第5天', sendTimeMs=1565603365182}

可以看出時間發送時間,和定時發送時間基本一致,代碼中Msg需要實現Delayed接口,重點在於getDelay方法,這個方法返回剩餘的延遲時間,代碼中使用this.sendTimeMs減去當前時間的毫秒格式時間,得到剩餘延遲時間。

LinkedTransferQueue

LinkedTransferQueue是一個由鍊表結構組成的無界阻塞TransferQueue隊列。相對於其他阻塞隊列,LinkedTransferQueue多了tryTransfer和transfer方法。

LinkedTransferQueue類繼承自AbstractQueue抽象類,並且實現了TransferQueue接口:

public interface TransferQueue<E> extends BlockingQueue<E> {
    // 如果存在一個消費者已經等待接收它,則立即傳送指定的元素,否則返回false,並且不進入隊列。
    boolean tryTransfer(E e);
    // 如果存在一個消費者已經等待接收它,則立即傳送指定的元素,否則等待直到元素被消費者接收。
    void transfer(E e) throws InterruptedException;
    // 在上述方法的基礎上設置超時時間
    boolean tryTransfer(E e, long timeout, TimeUnit unit)
        throws InterruptedException;
    // 如果至少有一位消費者在等待,則返回true
    boolean hasWaitingConsumer();
    // 獲取所有等待獲取元素的消費線程數量
    int getWaitingConsumerCount();
}

再看一下上面的這些方法,transfer(E e)方法和SynchronousQueue的put方法類似,都需要等待消費者取走元素,否者一直等待。其他方法和ArrayBlockingQueue、LinkedBlockingQueue中的方法類似。

總結

重點需要了解BlockingQueue中的所有方法,以及他們的區別

重點掌握ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue的使用場景

需要處理的任務有優先級的,使用PriorityBlockingQueue

處理的任務需要延時處理的,使用DelayQueue

java高並發系列目錄

第1天:必須知道的幾個概念

第2天:並發級別

第3天:有關並行的兩個重要定律

第4天:JMM相關的一些概念

第5天:深入理解進程和線程

第6天:線程的基本操作

第7天:volatile與Java內存模型

第8天:線程組

第9天:用戶線程和守護線程

第10天:線程安全和synchronized關鍵字

第11天:線程中斷的幾種方式

第12天JUC:ReentrantLock重入鎖

第13天:JUC中的Condition對象

第14天:JUC中的LockSupport工具類,必備技能

第15天:JUC中的Semaphore(信號量)

第16天:JUC中等待多線程完成的工具類CountDownLatch,必備技能

第17天:JUC中的循環柵欄CyclicBarrier的6種使用場景

第18天:JAVA線程池,這一篇就夠了

第19天:JUC中的Executor框架詳解1

第20天:JUC中的Executor框架詳解2

第21天:java中的CAS,你需要知道的東西

第22天:JUC底層工具類Unsafe,高手必須要了解

第23天:JUC中原子類,一篇就夠了

第24天:ThreadLocal、InheritableThreadLocal(通俗易懂)

相關焦點

  • Java【多線程系列】JUC線程池—2. 原理(二)、Callable和Future
    在"Java多線程系列--「基礎篇」01之 基本概念"中,我們介紹過,線程有5種狀態:新建狀態,就緒狀態,運行狀態,阻塞狀態,死亡狀態。
  • 源碼解析:阻塞隊列 LinkedBlockingQueue
    LinkedBlockingQueue是一種近似有界阻塞隊列,為什麼說近似?public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { /** * 隊列容量.
  • 阻塞隊列實現之LinkedBlockingQueue源碼解析
    隊列中的元素通過AtomicInteger類型的原子變量count記錄。維護兩把鎖:takeLock保證同時只有一個線程可以從對頭獲取元素,putLock保證只有一個線程可以在隊尾插入元素。維護兩個條件變量:notEmpty和notFull,維護條件隊列,用以存放入隊出隊阻塞的線程。❝如果希望獲取一個元素,需要先獲取takeLock鎖,且notEmpty條件成立。
  • 如何學習Java,哪裡開始學Java比較好?
    哪裡開始學Java比較好?當然是從基礎開始學習咯,學習需要循序漸進  新手前期主要是安裝開發工具,基本入手步驟:  開發環境搭建,安裝JDK  安裝開發工具 - eclipse  買書籍或者看電子書:學習Java有哪些值得推薦的Java書籍?
  • 並發隊列:無界阻塞隊列 LinkedBlockingQueue 原理探究
    超時前退出循環後,就從隊列移除元素,然後計數器減去一,如果減去1前隊列元素大於1則說明當前移除後隊列還有元素,那麼就發信號激活其他可能阻塞到當前條件信號的線程。最後如果減去1前隊列元素個數=最大值,那麼移除一個後會騰出一個空間來,這時候可以激活可能存在的入隊阻塞線程。
  • Python 內置的隊列 Queue
    四種 Queue 類1. class queue.Queue(maxsize=0)先進先出類型,maxsize 是個整數,用於設置可以放入隊列中的元素數的上限。當達到這個大小的時候,插入操作將阻塞至隊列中的元素被消費掉。如果 maxsize 小於等於零,隊列長度為無限大。
  • Java並發編程系列21|Condition-Lock的等待通知
    false,下文詳解isOnSyncQueue()方法 * 2.如果node不在AQS同步隊列中,將當前線程阻塞 * 3.當其他代碼調用signal()方法,線程進入AQS同步隊列後被喚醒,繼續從這裡阻塞的地方開始執行 * 4.注意這裡while循環的自旋,線程被喚醒以後還要再檢查一下node是否在
  • Java並發編程:如何防止在線程阻塞與喚醒時死鎖
    當線程到達監控對象時,通過wait方法會使線程進入到等待隊列中。而當其它線程調用notify時則可以使線程重新回到執行隊列中,得以繼續執行 使用wait與notify能在一定程度上避免死鎖問題,但並不能完全避免,它要求我們必須在編程過程中避免死鎖。在使用過程中需要注意的幾點是: 首先,wait與notify方法是針對對象的,調用任意對象的wait()方法都將導致線程阻塞,阻塞的同時也將釋放該對象的鎖。
  • 2021 必須掌握的 21個Java 核心技術
    這個知識點是最最基本的java開發者需要掌握的,第一個肯定是教你如何在命令行中執行java程序,但是很多人一旦把java學完了,IDE用上了,就把這個都忘了。 為什麼強調要知道這個呢,知道了java最純粹的啟動方式之後,你才能在啟動出問題的時候,去分析當時啟動的目錄多少,執行命名如何,參數如何,是否有缺失等。
  • 超硬核的Java學習路線指南,看完以後不用再問我怎麼學Java了!
    我們都知道編程技術語言很多,如當下比較火的程式語言就有java,python,javascript,php等語言,而今天我們就來講一講熱門程式語言Java,因為現階段我們的java程序還是很厲害的,不管是大型項目、高並發上億的數據量還是操作小項目,其穩定性,安全性都是數一數二的,非常nice!!
  • Java精講 | 45張圖庖丁解牛18種Queue,你知道幾種?
    他們都是java.util包這個大家庭的成員哦~1.2 現實生活中的場景1.3 計算機世界中的場景UDP協議(接收端將消息存放在隊列中,從隊列中讀取數據)二、高屋建瓴,縱覽全局18種隊列分為三大類: 接口、抽象類、普通類。
  • Java中 休眠(sleep)
    首先準備eclipse(這只是作者用的哦,還有很多就不做具體介紹了),然後點擊File --> new -->Javaproject,然後給自己的Javaproject取一個名字,我們在這裡取名為GetTime,然後打開新建一個package名為sleep的package,然後在package中新建一個名為sleepTime的class,如圖:接下來就開始進行代碼實現
  • 開發崗位這麼多,為什麼選Java?你學Java了嗎-開課吧
    提到C++語言,很多人發現在使用過程中最容易出現的錯誤就是內存管理,而java有自動垃圾回收器,不用擔心內存。零基礎學Java作為一門面向對象的高級語言,Java不僅吸收了C++語言的各種優點,還對C++裡諸如多繼承、指針等一些讓用戶難以理解和掌握的概念重新組織和及摒棄,從而形成了Java
  • 「正點原子Linux連載」第五十二章Linux阻塞和非阻塞IO實驗
    第五十二章Linux阻塞和非阻塞IO實驗阻塞和非阻塞IO是Linux驅動開發裡面很常見的兩種設備訪問模式,在編寫驅動的時候一定要考慮到阻塞和非阻塞。本章我們就來學習一下阻塞和非阻塞IO,以及如何在驅動程序中處理阻塞與非阻塞,如何在驅動程序使用等待隊列和poll機制。
  • 零基礎學java,常見的誤區和解決方法
    造成這樣的結果說白了還是技術學得不好,沒有掌握正確的學習方法。 我的個人建議: 1,首先零基礎學習java先要有一個學習計劃,了解java要學習哪些技術。
  • 深入理解 JUC:LinkedBlockingQueue
    針對一些阻塞版本的出隊列入隊列方法,如果隊列為空,則出隊列線程會被記錄到條件隊列 notEmpty 中進行等待,如果隊列已滿,則入隊列線程會被記錄到條件隊列 notFull 中進行等待。BlockingQueue 接口BlockingQueue 接口繼承自 Queue 接口,用於描述阻塞隊列。
  • Java並發編程系列20|StampedLock源碼解析
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文為何適原創並發編程系列第 20 篇,文末有本系列文章匯總。寫鎖:state 第 8 位為寫鎖標誌,0 表示未被佔用,1 表示寫鎖被佔用。state 第 8-64 位表示寫鎖的獲取次數,次數超過 64 位最大容量則重新從 1 開始。樂觀讀鎖:不需要維護鎖狀態,但是在具體操作數據前要檢查一下自己操作的數據是否經過修改操作,也就是驗證是否有線程獲取過寫鎖。
  • RabbitMQ實戰教程(一)——簡單隊列和工作隊列
    生產者可以不斷往消息隊列裡寫消息。消費者可以從隊列裡消費消息。2、MQ有什麼好處生產者不用關心消費者有沒有消費,只用把消息發到消息隊列裡就可以了。消費者不用關心生產者有沒有發消息,只關心隊列裡有沒有消息就可以了。整個過程沒有API侵入,輕鬆實現了系統之間的解耦。
  • 給Java新手的一些建議——Java知識點歸納(Java基礎部分)
    Java的運行(基礎必備)這條可能出看很簡單,java程序的運行誰不會呢?不過很多時候, 我們只是單純通過IDE去執行java程序,底層IDE又是如何執行java程序呢?很多人並不了解。這個知識點是最最基本的java開發者需要掌握的,初學java,第一個肯定是教你如何在命令行中執行java程序,但是很多人一旦把java學完了,IDE用上了,就把這個都忘了。
  • Python Queue隊列實現線程通信
    在 queue 模塊下主要提供了三個類,分別代表三種隊列,它們的主要區別就在於進隊列、出隊列的不同。關於這三個隊列類的簡單介紹如下:queue.Queue(maxsize=0):代表 FIFO(先進先出)的常規隊列,maxsize 可以限制隊列的大小。如果隊列的大小達到隊列的上限,就會加鎖,再次加入元素時就會被阻塞,直到隊列中的元素被消費。