BlockingQueue 阻塞队列

队列就是存放元素的容器,FIFO。

所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤起。

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。

常用于生产者和消费者的场景,生产者---》往队列里添加元素,消费者---》从队列里拿取元素。

在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。

阻塞队列提供了四种处理方法:

方式 抛出异常 不抛出异常,有返回值 阻塞等待 超时等待
添加 add() offer() put() offer()
移除 remove() poll() take() poll()
检测队首元素 element() peek() ~ ~

阻塞队列的成员

ArrayBlockingQueue:是一个用数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。支持公平锁和非公平锁。

LinkedBlockingQueue:一个由链表结构组成的有界队列,此队列的长度为Integer.MAX_VALUE。此队列按照先进先出的顺序进行排序。

SynchronousQueue:一个不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素。支持公平锁和非公平锁。

没有容量,一个生产线程,当它生产产品(put的时候),如果当前没有人想要消费产品(即当前没有线程执行take),此生产线程必须阻塞。等待一个消费线程调用take操作,take操作会唤醒该生产线程,同时消费线程会获取生产线程的产品(即数据传递)。这样的一个过程称为一次配对过程(也可以先take,后put 原理相同)。

ArrayBlockingQueue和LinkedBlockingQueue是两个最普通也是最常用的阻塞队列,一般情况下,在处理多线程间的生产者消费者问题,使用这两个类足以。

使用举例

生产者

5  
 6 /**
 7  * 生产者线程
 8  * 
 9  * 
10  */
11 public class Producer implements Runnable {
12     
13     private volatile boolean  isRunning = true;//是否在运行标志
14     private BlockingQueue queue;//阻塞队列
15     private static AtomicInteger count = new AtomicInteger();//自动更新的值
16     private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;
17  
18     //构造函数
19     public Producer(BlockingQueue queue) {
20         this.queue = queue;
21     }
22  
23     public void run() {
24         String data = null;
25         Random r = new Random();
26  
27         System.out.println("启动生产者线程!");
28         try {
29             while (isRunning) {
30                 System.out.println("正在生产数据...");
31                 Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP));//取0~DEFAULT_RANGE_FOR_SLEEP值的一个随机数
32  
33                 data = "data:" + count.incrementAndGet();//以原子方式将count当前值加1
34                 System.out.println("将数据:" + data + "放入队列...");
35                 if (!queue.offer(data, 2, TimeUnit.SECONDS)) {//设定的等待时间为2s,如果超过2s还没加进去返回true
36                     System.out.println("放入数据失败:" + data);
37                 }
38             }
39         } catch (InterruptedException e) {
40             e.printStackTrace();
41             Thread.currentThread().interrupt();
42         } finally {
43             System.out.println("退出生产者线程!");
44         }
45     }
46  
47     public void stop() {
48         isRunning = false;
49     }
50 }

消费者

 5 /**
 6  * 消费者线程
 7  * 
 8  * 
 9  */
10 public class Consumer implements Runnable {
11     
12     private BlockingQueue<String> queue;
13     private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;
14  
15     //构造函数
16     public Consumer(BlockingQueue<String> queue) {
17         this.queue = queue;
18     }
19  
20     public void run() {
21         System.out.println("启动消费者线程!");
22         Random r = new Random();
23         boolean isRunning = true;
24         try {
25             while (isRunning) {
26                 System.out.println("正从队列获取数据...");
27                 String data = queue.poll(2, TimeUnit.SECONDS);//有数据时直接从队列的队首取走,无数据时阻塞,在2s内有数据,取走,超过2s还没数据,返回失败
28                 if (null != data) {
29                     System.out.println("拿到数据:" + data);
30                     System.out.println("正在消费数据:" + data);
31                     Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP));
32                 } else {
33                     // 超过2s还没数据,认为所有生产线程都已经退出,自动退出消费线程。
34                     isRunning = false;
35                 }
36             }
37         } catch (InterruptedException e) {
38             e.printStackTrace();
39             Thread.currentThread().interrupt();
40         } finally {
41             System.out.println("退出消费者线程!");
42         }
43     }
44  
45     
46 }

 

测试

public class BlockingQueueTest {
 7  
 8     public static void main(String[] args) throws InterruptedException {
 9         // 声明一个容量为10的缓存队列
10         BlockingQueue<String> queue = new LinkedBlockingQueue<String>(10);
11  
12         //new了三个生产者和一个消费者
13         Producer producer1 = new Producer(queue);
14         Producer producer2 = new Producer(queue);
15         Producer producer3 = new Producer(queue);
16         Consumer consumer = new Consumer(queue);
17  
18         // 借助Executors
19         ExecutorService service = Executors.newCachedThreadPool();
20         // 启动线程
21         service.execute(producer1);
22         service.execute(producer2);
23         service.execute(producer3);
24         service.execute(consumer);
25  
26         // 执行10s
27         Thread.sleep(10 * 1000);
28         producer1.stop();
29         producer2.stop();
30         producer3.stop();
31  
32         Thread.sleep(2000);
33         // 退出Executor
34         service.shutdown();
35     }
36 }

 

SynchronizedQueue举例:

package test;

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

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+" put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()+" put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()+" put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"take"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"take"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"take"+blockingQueue.take());


            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T2").start();

    }
}
结果:
T1 put 1
T2take1
T1 put 2
T2take2
T1 put 3
T2take3

小结

作为BlockingQueue的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了.

BlockingQueue不光实现了一个完整队列所具有的基本功能,同时在多线程环境下,他还自动管理了多线间的自动等待于唤醒功能,从而使得程序员可以忽略这些细节,关注更高级的功能。

posted @ 2021-08-23 11:17  精进的浩然兄  阅读(97)  评论(0编辑  收藏  举报