Java線程-阻塞隊列

阻塞隊列是在隊列基礎上又加了兩個附加操作的隊列,分別爲阻塞插入和阻塞移除。
阻塞插入:隊列滿時,隊列會阻塞插入元素的線程,直到隊列不滿
阻塞移除:隊列空時,獲取元素的線程會等待隊列變爲非空。
一個簡單例子:

public class Thread_Bdueue {
    public static void main(String args[]){
        BlockingDeque blockingDeque=new LinkedBlockingDeque(20);
        for (int i=0;i<30;i++){
            try {
                //指定元素添加到阻塞棧當中,如果沒有可用空間,則一直等待
                blockingDeque.putFirst(i);
                System.out.println("向阻塞棧當中添加了元素:"+i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("程序到此運行結束-------");
    }
}

應用場景:阻塞隊列應用於生產者消費者的場景,簡而言之,阻塞隊列是生產者存放元素,消費者消費元素的容器。

方法\處理方式 拋出異常 返回特殊值 一直阻塞 超時退出
插入方法 add(e) offer(e) put(e) offer(e,time,unit)
移除方法 remove() poll() take() poll(time,unit)
檢查方法 element() peek() 不可用 不可用

JDK7提供了7個阻塞隊列
(1)、ArrayBlockingQueue :一個由數組結構組成的有界阻塞隊列。
此隊列按照先進先出的原色對元素進行排序,默認情況下不保證公平的訪問隊列。即如果隊列滿了,那麼被阻塞在外面的線程對隊列訪問的順序是不能保證線程公平(即先阻塞,先插入)的。
(2)、LinkedBlockingQueue :一個由鏈表結構組成的有界阻塞隊列。此隊列按照先出先進的原則對元素進行排序
(3)、PriorityBlockingQueue :一個支持優先級排序的無界阻塞隊列。
(4)、DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
(5)、SynchronousQueue:一個不存儲元素的阻塞隊列。每一個put必須等待一個take操作,否則不能繼續添加元素。並且他支持公平訪問隊列。
(6)、LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
(7)、LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。優勢在於多線程入隊時,減少一半的競爭。

使用BlockingQueue解決生產者和消費者的問題
任何有效的生產者-消費者問題解決方案都是通過控制生產者put()方法(生產資源)和消費者take()方法(消費資源)的調用來實現的,一旦你實現了對方法的阻塞控制,那麼你將解決該問題。

Java通過BlockingQueue提供了開箱即用的支持來控制這些方法的調用(一個線程創建資源,另一個消費資源)。java.util.concurrent包下的BlockingQueue接口是一個線程安全的可用於存取對象的隊列。
生產者

public class Producer implements Runnable {
    protected BlockingQueue<Object> queue;

    Producer(BlockingQueue<Object> theQueue) {
        this.queue = theQueue;
    }
    
    Object getResource() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            System.out.println("生產者讀中斷");
        }
        return new Object();
    }

    @Override
    public void run() {
        try {
            while (true) {
                Object justProduced = getResource();
                queue.put(justProduced);
                System.out.println("生產者資源隊列大小= " + queue.size());
            }
        } catch (InterruptedException ex) {
            System.out.println("生產者中斷");
        }
    }
}

消費者

public class Consumer implements Runnable  {

    protected BlockingQueue<Object> queue;
    Consumer(BlockingQueue<Object> theQueue) {
        this.queue = theQueue;
    }
    void take(Object obj) {
        try {
            Thread.sleep(100); // simulate time passing
        } catch (InterruptedException ex) {
            System.out.println("消費者讀中斷");
        }
        System.out.println("消費對象" + obj);
    }

    @Override
    public void run() {
        try {
            while (true) {
                Object obj = queue.take();
                System.out.println("消費者資源隊列大小 " + queue.size());
                take(obj);
            }
        } catch (InterruptedException ex) {
            System.out.println("消費者中斷");
        }
    }
}

執行者

public class Main {
    public static void main(String args[]) throws InterruptedException {
        int numProducers = 4;
        int numConsumers = 3;
        BlockingQueue<Object> myQueue = new LinkedBlockingQueue<Object>(5);
        for (int i = 0; i < numProducers; i++) {
            new Thread(new Producer(myQueue)).start();
        }
        for (int i = 0; i < numConsumers; i++) {
            new Thread(new Consumer(myQueue)).start();
        }
        Thread.sleep(1000);
        System.exit(0);
    }
}

運行結果如下:


生產者資源隊列大小= 1
生產者資源隊列大小= 1
消費者 資源 隊列大小 1
生產者資源隊列大小= 1
消費者 資源 隊列大小 1
消費者 資源 隊列大小 1
生產者資源隊列大小= 1
生產者資源隊列大小= 3
消費對象 java.lang.Object@1e1aa52b
生產者資源隊列大小= 2
生產者資源隊列大小= 5
消費對象 java.lang.Object@6e740a76
消費對象 java.lang.Object@697853f6
 
......
 
消費對象 java.lang.Object@41a10cbc
消費對象 java.lang.Object@4963c8d1
消費者 資源 隊列大小 5
生產者資源隊列大小= 5
生產者資源隊列大小= 5
消費者 資源 隊列大小 4
消費對象 java.lang.Object@3e49c35d
消費者 資源 隊列大小 4

從輸出結果中,我們可以發現隊列大小永遠不會超過5,消費者線程消費了生產者生產的資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章