1. 程式人生 > >java多執行緒-03-阻塞佇列簡介

java多執行緒-03-阻塞佇列簡介

宣告

該系列文章只是記錄本人回顧java多執行緒程式設計時候記錄的筆記。文中所用語言並非嚴謹的專業術語(太嚴謹的術語其實本人也不會……)。難免有理解偏差的地方,歡迎指正。
另外,大神請繞路。不喜勿噴。
畢竟好記性不如爛筆頭嘛,而且許多東西只要不是你經常用的最終都會一丟丟一丟丟地給忘記。

初看之下,阻塞佇列似乎和多執行緒沒有多大關係。但是平時使用阻塞佇列的場景往往是和執行緒相關的。所以,此處將阻塞佇列也歸入java多執行緒的分類。

1 什麼是阻塞佇列

說白了,阻塞佇列還是一個佇列(FIFO)。至於佇列的概念就不多說了。
阻塞佇列和普通佇列的區別就在阻塞這個詞。

阻塞的意思,可以這麼理解:

  • 當嘗試隊列出隊操作時,若佇列已經是空的,則調用出隊操作的執行緒將被阻塞,直到佇列有可用元素為止
  • 當嘗試佇列入隊操作時,若佇列已經是滿的,則呼叫入隊操作的執行緒將被阻塞,直到佇列不再是滿的為止

2 JDK提供的阻塞佇列

2.1 JDK內建的阻塞佇列

  • ArrayBlockingQueue: 陣列結構的有界阻塞佇列
  • LinkedBlockingQueue: 連結串列結構的有界阻塞佇列
  • LinkedTransferQueue: 連結串列結構的無界阻塞佇列
  • LinkedBlockingDeque: 連結串列結構的雙向阻塞佇列
  • PriorityBlockingQueue: 支援按優先順序
    排序的無界阻塞佇列
  • DelayQueue: 使用優先順序佇列(PriorityQueue)實現的阻塞佇列,優先佇列的比較基準值是時間
  • SynchronousQueue: 不知道怎麼描述這個佇列,因為他並不真正儲存元素。每個插入操作必須等待另一個執行緒的移除操作,同樣一個移除操作都等待另一個執行緒的插入操作。

2.2 阻塞佇列不可用時的處理方式

此處要說的是,佇列在特殊情況下的出隊入隊操作的處理。
比如在佇列為空或者佇列已經滿了的時候不同的阻塞佇列實現或者不同的方法會有不同的處理方式。

對這種特殊情況的處理,大致有以下一些方法:

  • 拋異常
    • 佇列為空時進行出隊操作,可能會有NoSuchElementException
    • 佇列已滿時進行入隊操作,可能會有IllegalStateException
  • 返回特殊值
    • 丟擲NoSuchElementException,可以返回null代替表示佇列為空
    • 丟擲IllegalStateException,可以返回false表示入隊失敗
  • 一直阻塞
    • 佇列為空時進行出隊操作,進行出隊操作的執行緒將被阻塞,直至佇列可用
    • 佇列已滿時進行入隊操作,進行入隊操作的執行緒將被阻塞,直至佇列可用
  • 超時退出
    • 佇列為空時進行出隊操作,進行出隊操作的執行緒將被阻塞,直至超時退出(執行緒也會退出)
    • 佇列已滿時進行入隊操作,進行入隊操作的執行緒將被阻塞,直至超時退出(執行緒也會退出)

以下圖片是來自《Java併發程式設計的藝術》一書中的總結:

這佇列特殊情況的處理

3 使用示例

以上介紹的七種JDK內建的阻塞佇列,各有各的使用場景,一篇文章很難介紹清楚。
以後會抽空補全各種阻塞佇列的使用場景示例。

和上篇文章的不同之處在於,此處使用阻塞佇列來代替上篇文章中簡單實現的一個數據結構棧。
Container類的實現修改成了下面的樣子:

public static class Container {
    private ArrayBlockingQueue<Product> products;

    public Container(int size) {
        this.products = new ArrayBlockingQueue<>(size);
    }

    public void put(Product product) {
        try {
            this.products.put(product);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public Product get() {
        try {
            return this.products.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

完整的程式碼示例如下:

import java.util.concurrent.ArrayBlockingQueue;

public class ProducerConsumerByLockingQueue {

    public static void main(String[] args) {
        Container container = new Container(5);
        for (int i = 0; i < 10; i++) {
            new Thread(new Producer(container), "P-" + i).start();
            new Thread(new Consumer(container), "C-" + i).start();
        }
    }

    public static class Product {
        private String name;

        public Product(String name) {
            super();
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "[name=" + name + "]";
        }

    }

    public static class Container {
        private ArrayBlockingQueue<Product> products;

        public Container(int size) {
            this.products = new ArrayBlockingQueue<>(size);
        }

        public void put(Product product) {
            try {
                this.products.put(product);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public Product get() {
            try {
                return this.products.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    public static class Producer implements Runnable {

        private Container container;

        public Producer(Container container) {
            super();
            this.container = container;
        }

        @Override
        public void run() {
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                Product p = new Product(Thread.currentThread().getName() + "_" + i);
                this.container.put(p);
                System.out.println("生產者[" + Thread.currentThread().getName() + "]生產>>>>>>:" + p);
                try {
                    Thread.sleep((long) (Math.random() * 1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static class Consumer implements Runnable {

        private Container container;

        public Consumer(Container container) {
            super();
            this.container = container;
        }

        @Override
        public void run() {
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                Product product = this.container.get();
                System.out.println("消費者[" + Thread.currentThread().getName() + "]消費<<<<<<:" + product);
                try {
                    Thread.sleep((long) (Math.random() * 2000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

參考資料

  • 《java併發程式設計的藝術》

相關推薦

java執行-03-阻塞佇列簡介

宣告 該系列文章只是記錄本人回顧java多執行緒程式設計時候記錄的筆記。文中所用語言並非嚴謹的專業術語(太嚴謹的術語其實本人也不會……)。難免有理解偏差的地方,歡迎指正。 另外,大神請繞路。不喜勿噴。 畢竟好記性不如爛筆頭嘛,而且許多東西只要不是

Java執行-BlockingQueue(阻塞佇列)

前言:   BlockingQueue是多執行緒安全的佇列,它有兩種常見的阻塞場景。    佇列中沒有資料的情況下,消費者端的所有執行緒都會被自動阻塞(掛起),直到有資料放入佇列。 當佇列中填滿

Java執行____BlockingQueue阻塞佇列使用

package com.frame.base.thread; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ArrayBlockingQueue; /** * 併發程式設計

Java執行之happen-before簡介

在JDK5 開始,Java使用新的JSR-133記憶體模型,該模型使用happens-before的概念來闡述操作之間的記憶體可見性。在JMM中,如果一個操作執行的結果需要對另一個操作可見,那麼這兩個操作之間必須要存在happens-before關係。這裡提到的兩個操作既可以是一個執行緒之內,也可以

18_張孝祥_執行_阻塞佇列的應用

類相關屬性 介面BlockingQueue<E>定義: public interface BlockingQueue<E> extends Queue<E> { boolean add(E e);

執行消費阻塞佇列(生產者消費者模型)

一.幾種主要的阻塞佇列ArrayBlockingQueue:基於陣列實現的一個阻塞佇列,在建立ArrayBlockingQueue物件時必須制定容量大小。並且可以指定公平性與非公平性,預設情況下為非公平的,即不保證等待時間最長的佇列最優先能夠訪問佇列。LinkedBlocki

thrift java執行阻塞同步/非同步呼叫例項

作者:呂桂強 郵箱:[email protected] 首先建立thrift檔案 namespace java thriftservice Hello{  string helloString(1:string para)} 執行thrift -ge

Java執行---阻塞佇列詳解(舉例說明)

一. 前言   在新增的Concurrent包中,BlockingQueue很好的解決了多執行緒中,如何高效安全“傳輸”資料的問題。通過這些高效並且執行緒安全的佇列類,為我們快速搭建高質量的多執行緒程式帶來極大的便利。本文詳細介紹了BlockingQueue家庭中的所有成員

Java執行程式設計---java5阻塞佇列

java5阻塞佇列的應用        佇列包含固定長度的佇列和不固定長度的佇列,先進先出。        固定長度的佇列往裡放資料,如果放滿了還要放,阻塞式佇列就會等待,直到有資料取出,空出位置後才繼續放;非阻塞式佇列不能等待就只能報錯了。        講Conditio

Java執行 阻塞佇列和併發集合

本章主要探討在多執行緒程式中與集合相關的內容。在多執行緒程式中,如果使用普通集合往往會造成資料錯誤,甚至造成程式崩潰。Java為多執行緒專門提供了特有的執行緒安全的集合類,通過下面的學習,您需要掌握這些集合的特點是什麼,底層實現如何、在何時使用等問題。 3.1Blockin

Java執行之模擬一個阻塞佇列

import java.util.LinkedList; import java.util.concurrent.atomic.AtomicInteger; public class MyQueue { private final LinkedLi

Java執行-生產者消費者例子-使用阻塞佇列(BlockingQueue)實現

import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * Created by wisgood . */ public

java執行併發處理之阻塞佇列LinkedBlockingQueue用法

<pre name="code" class="java">public class ThreadRelatedService2 { // key point: records list must be ordered public void

(八) Java執行詳解之阻塞佇列BlockingQueue及佇列優先順序詳解

阻塞佇列 阻塞佇列與普通佇列的區別在於當佇列是空時從佇列中獲取元素的操作將會被阻塞,或者當佇列是滿時往佇列裡新增元素的操作會被阻塞。試圖從空的阻塞佇列中獲取元素的執行緒將會被阻塞,直到其他的執行緒往空的佇列插入新的元素,同樣試圖往已滿的阻塞佇列中新增新元素的執

Java執行中的阻塞佇列和併發集合

 本章主要探討在多執行緒程式中與集合相關的內容。在多執行緒程式中,如果使用普通集合往往會造成資料錯誤,甚至造成程式崩潰。Java為多執行緒專門提供了特有的執行緒安全的集合類,通過下面的學習,您需要掌握這些集合的特點是什麼,底層實現如何、在何時使用等問題。 3.1 Blo

跟我學Java執行——執行池與阻塞佇列

前言     上一篇文章中我們將ThreadPoolExecutor進行了深入的學習和介紹,實際上我們在專案中應用的時候很少有直接應用ThreadPoolExecutor來建立執行緒池的,在jdk的api中有這麼一句話“但是,強烈建議程式設計師使用較為方便的 Execu

Java執行/併發26、阻塞佇列BlockingQueue

BlockingQueue介面定義了一種佇列,這種佇列通常容量是提前固定(確定了容量大小)的。容量滿時往BlockingQueue中新增資料時會造成阻塞,容量為空時取元素操作會阻塞。 我們可以認為BlockingQueue佇列是一個水庫。水庫滿了的時侯,上游的

最全java執行總結3——瞭解阻塞佇列執行安全集合不

  看了前兩篇你肯定已經理解了 java 併發程式設計的低層構建。然而,在實際程式設計中,應該經可能的遠離低層結構,畢竟太底層的東西用起來是比較容易出錯的,特別是併發程式設計,既難以除錯,也難以發現問題,我們還是使用由併發處理的專業人員實現的較高層次的結構要方便、安全得多。 阻塞佇列   對於許多執行緒問題,

java執行使用BlockingQueue阻塞佇列實現互斥同步通訊

package com.study; import java.util.concurrent.ArrayBlockingQu

JAVA執行使用JDK1.5提供的Lock,Condition手寫阻塞佇列

package com.study; import java.util.Random; import java.util.