【java并发】阻塞队列的使用

  在前面一篇名为条件阻塞Condition的应用的博客中提到了一个拔高的例子:利用Condition来实现阻塞队列。其实在java中,有个叫ArrayBlockingQueue<E>的类提供了阻塞队列的功能,所以我们如果需要使用阻塞队列,完全没有必要自己去写。

  ArrayBlockingQueue<E>实现了BlockingQueue<E>,另外还有LinkedBlockingQueue<E>PriorityBlockingQueue<E>ArrayBlockingQueue<E>主要是基于数组实现的,LinkedBlockingQueue<E>是基于链表实现的,它们都是先进先出;而PriorityBlockingQueue<E>是基于优先级队列的。这篇博文主要用ArrayBlockingQueue<E>举例,另外两个使用方式和ArrayBlockingQueue<E>差不多,具体的可以参考官方文档。

  使用ArrayBlockingQueue<E>需要先指明缓存区的大小,指明之后就无法修改,试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。下面使用ArrayBlockingQueue<E>来实现类似于前面那篇博客中提到的一个功能:

public class BlockingQueueTest {

    public static void main(String[] args) {
        final BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3); //缓冲区允许放3个数据

        for(int i = 0; i < 2; i ++) {
            new Thread() { //开启两个线程不停的往缓冲区存数据

                @Override
                public void run() {
                    while(true) {
                        try {
                            Thread.sleep((long) (Math.random()*1000));
                            System.out.println(Thread.currentThread().getName() + "准备放数据"
                                    + (queue.size() == 3?"..队列已满,正在等待":"..."));
                            queue.put(1);
                            System.out.println(Thread.currentThread().getName() + "存入数据,"
                                    + "队列目前有" + queue.size() + "个数据");
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }

            }.start();
        }

        new Thread() { //开启一个线程不停的从缓冲区取数据

            @Override
            public void run() {
                while(true) {
                    try {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "准备取数据"
                                + (queue.size() == 0?"..队列已空,正在等待":"..."));
                        queue.take();
                        System.out.println(Thread.currentThread().getName() + "取出数据,"
                                + "队列目前有" + queue.size() + "个数据");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

}

  程序的逻辑很简单,不难理解,下面看一下运行的结果:

Thread-0准备放数据…

Thread-0存入数据,队列目前有1个数据

Thread-1准备放数据…

Thread-1存入数据,队列目前有2个数据

Thread-2准备取数据…

Thread-2取出数据,队列目前有1个数据

Thread-0准备放数据…

Thread-0存入数据,队列目前有2个数据

Thread-1准备放数据…

Thread-1存入数据,队列目前有3个数据

Thread-2准备取数据…

Thread-2取出数据,队列目前有2个数据

Thread-0准备放数据…

Thread-0存入数据,队列目前有3个数据

Thread-1准备放数据..队列已满,正在等待

Thread-0准备放数据..队列已满,正在等待

Thread-2准备取数据…

Thread-2取出数据,队列目前有2个数据

Thread-1存入数据,队列目前有3个数据

  所以使用阻塞队列就非常方便了,不用我们人为的去做判断了。

  之前在博客里介绍过线程间通信可以使用synchronized和wait、notify组合,可以使用Condition,其实我们也可以使用阻塞队列来实现线程间的通信,下面举个示例:

//阻塞队列实现线程间通信
public class BlockingQueueCommunication {

    public static void main(String[] args) {
        Business bussiness = new Business();

        new Thread(new Runnable() {// 开启一个子线程

                    @Override
                    public void run() {
                        for (int i = 1; i <= 3; i++) {

                            bussiness.sub();
                        }

                    }
                }).start();

        // main方法主线程
        for (int i = 1; i <= 3; i++) {

            bussiness.main();
        }
    }
}

class Business {

    private int i = 0;

    BlockingQueue queue1 = new ArrayBlockingQueue(1);
    BlockingQueue queue2 = new ArrayBlockingQueue(1);

    {
        try {
            queue2.put(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void sub() {
        try {
            queue1.put(1); //如果主线程没执行完,则子线程缓冲区一直有数,子线程在这里被阻塞
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("子线程执行前i=" + i++);
        System.out.println("子线程执行后i=" + i);

        try {
            queue2.take(); //取出主线程中缓冲区的数,让主线程执行
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void main() {
        try {
            queue2.put(1); //如果子线程没执行完,则主线程缓冲区一直有数,主线程在这里被阻塞
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("主线程执行前i=" + i++);
        System.out.println("主线程执行后i=" + i);

        try {
            queue1.take(); //取出子线程中缓冲区的数,让子线程执行
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

  代码看起来有点长,但是逻辑很简单,就是主线程和子线程你一下我一下,轮流执行,执行的任务就是将公共数据i自增1,使用阻塞队列实现的,而且线程安全,因为一个线程执行的时候,另一个线程是被阻塞的。

  设计思想是这样的,定义两个阻塞队列,分别只能放1个数,分别对应两个线程。那么我在静态代码块中先将主线程的队列塞满,然后我让两个线程在执行任务之前,先往各自的队列中塞一个数,那么肯定主线程肯定被阻塞,因为我之前已经在静态代码块中给主线程的队列塞过一个数了。然后子线程在执行完任务后,把主线程队列中的数取出,那么主线程就开始执行了,子线程此时被阻塞(因为刚刚它自己执行任务前塞了个数),主线程执行完拿出子线程队列中的数,这时候子线程又开始执行了。所以利用了阻塞队列会阻塞一个线程的办法来实现两个线程之间交替执行。

  关于阻塞队列的使用,就总结这么多吧!

  

  相关阅读:http://blog.csdn.net/column/details/bingfa.html



—–乐于分享,共同进步!

—–更多文章请看:http://blog.csdn.net/eson_15

时间: 2024-10-05 23:27:05

【java并发】阻塞队列的使用的相关文章

深入浅出 Java Concurrency (25): 并发容器 part 10 双向并发阻塞队列 BlockingDeque[转]

这个小节介绍Queue的最后一个工具,也是最强大的一个工具.从名称上就可以看到此工具的特点:双向并发阻塞队列.所谓双向是指可以从队列的头和尾同时操作,并发只是线程安全的实现,阻塞允许在入队出队不满足条件时挂起线程,这里说的队列是指支持FIFO/FILO实现的链表. 首先看下LinkedBlockingDeque的数据结构.通常情况下从数据结构上就能看出这种实现的优缺点,这样就知道如何更好的使用工具了. 从数据结构和功能需求上可以得到以下结论: 要想支持阻塞功能,队列的容量一定是固定的,否则无法在

Java多线程 阻塞队列和并发集合

转载:大关的博客 Java多线程 阻塞队列和并发集合 本章主要探讨在多线程程序中与集合相关的内容.在多线程程序中,如果使用普通集合往往会造成数据错误,甚至造成程序崩溃.Java为多线程专门提供了特有的线程安全的集合类,通过下面的学习,您需要掌握这些集合的特点是什么,底层实现如何.在何时使用等问题. 3.1 BlockingQueue接口 java阻塞队列应用于生产者消费者模式.消息传递.并行任务执行和相关并发设计的大多数常见使用上下文. BlockingQueue在Queue接口基础上提供了额外

Java多线程——阻塞队列

现在,通过前几篇的总结,我们对Java多线程已经有所了解了,但是它们都是一些Java并发程序设计基础的底层构建块.对于实际编程来说,我们应该尽可能的远离底层结构.使用那些由并发处理的专业人士实现的较高层次的结构要方便的多,安全的多. 阻塞队列 对于许多线程问题.可以通过使用一个或多个队列以优雅且安全的方式将其形式化.生产者线程向队列插入元素,消费者线程则取出他们.使用队列,可以安全地从一个线程向另一个线程传递数据. 阻塞队列的方法 方法 正常动作 特殊情况下动作 add 添加一个元素 如果队列满

Java中阻塞队列的使用

http://blog.csdn.net/qq_35101189/article/details/56008342 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利.本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景. 认识BlockingQueue阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中所

java 可伸缩阻塞队列实现

最近一年多写的最虐心的代码.必须好好复习java并发了.搞了一晚上终于测试都跑通过了,特此纪念,以资鼓励! import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.

java 多线程阻塞队列 与 阻塞方法与和非阻塞方法

Queue是什么 队列,是一种数据结构.除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的.无论使用哪种排序方式,队列的头都是调用remove()或poll()移除元素的.在FIFO队列中,所有新元素都插入队列的末尾.队列都是线程安全的,内部已经实现安全措施,不用我们担心 Queue中的方法 Queue中的方法不难理解,6个,每2对是一个也就是总共3对.看一下JDK API就知道了: 注意一点就好,Queue通常不允许插入Null,尽管某些实现(比如Link

29、java中阻塞队列

阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素.同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列,下图展示了如何通过阻塞队列来合作: 线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素 使用BlockingQueue的关键技术点如

Java实现阻塞队列的两种方式

方式一:/** * 使用非阻塞队列PriorityQueue及wait/notify方法实现一个阻塞队列**/class MyBlockingQueue {    public final static int queueSize = 10;    public static final PriorityQueue<Integer> queue = new PriorityQueue();}    class Producer extends Thread {    public void r

JAVA实现阻塞队列

package 多线程并发; import java.util.Stack; /** * Created by z84102272 on 2018/7/17. */ public class BlockQueueImpl { private final static Object pushLock = new Object(); //push的锁 private final static Object popLock = new Object(); //pop的锁 private Stack<O

Java并发编程(十一)-- 阻塞队列

在介绍Java的阻塞队列之前,我们简单介绍一下队列. 队列 队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据,如果你试图向一个已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.在多线程进行合作时,阻塞队列是很有用的工具.工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线线程把中间结果取出并在将来修改它们.队列会自动平衡负载.如果第一个线程集运行得比第二个慢,则第二个 线程集