Java并发编程:阻塞队列


参考文章

非阻塞队列,如 PriorityQueueLinkedList (双向链表,实现了 Dequeue接口),使用的时候有一个很大的问题:它不会对当前线程产生阻塞,那么在面对 消费者-生产者模型 时,就必须额外的 实现同步策略 以及 线程间唤醒策略。这个实现起来非常麻烦。

阻塞队列会对当前线程产生阻塞,比如一个线程从一个 空的阻塞队列中取元素,此时线程会 被阻塞 直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会 自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。

1 几种主要的阻塞队列
  • ArrayBlockingQueue :基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列
  • LinkedBlockingQueue :基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE
  • PriorityBlockingQueue :以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。
  • DelayQueue :基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞
2 阻塞与非阻塞队列中的方法
  1. 非阻塞队列中的几个主要方法
  • add(E e):将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则会抛出异常
  • remove():移除队首元素,若移除成功,则返回true;如果移除失败(队列为空),则会抛出异常
  • offer(E e):将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则返回false
  • poll():移除并获取队首元素,若成功,则返回队首元素;否则返回null
  • peek():获取队首元素,若成功,则返回队首元素;否则返回null

对于非阻塞队列,一般情况下建议使用offerpollpeek 三个方法,不建议使用add和remove方法。因为使用offer、poll和peek三个方法可以通过返回值判断操作成功与否,而使用add和remove方法却不能达到这样的效果。注意,非阻塞队列中的方法都没有进行同步措施

  1. 阻塞队列中的几个主要方法
  • put(E e) : 向队尾存入元素,如果队列满,则等待
  • take() : 从队首取元素,如果队列为空,则等待
  • offer(E e,long timeout, TimeUnit unit) : 向队尾存入元素,如果队列满,则等待一定的时间,当时间期限达到时,如果还没有插入成功,则返回false;否则返回true
  • poll(long timeout, TimeUnit unit) : 从队首取元素,如果队列空,则等待一定的时间,当时间期限达到时,如果取到,则返回null;否则返回取得的元素

阻塞队列包括了非阻塞队列中的大部分方法,上面列举的5个方法在阻塞队列中都存在,但是要注意这5个方法在阻塞队列中都进行了同步措施

3 阻塞队列的实现原理

阻塞队列的实现原理 和 我们用Object.wait()、Object.notify()和非阻塞队列实现生产者-消费者的思路类似,只不过它把这些工作一起集成到了阻塞队列中实现。

4 示例和应用场景

阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列

  1. 先使用Object.wait()和Object.notify()、非阻塞队列实现生产者-消费者模式:

代码要点:
1 定义一个 非阻塞队列
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);

2 生产者和消费者都必须 实现同步策略 以及 线程间唤醒策略

消费者:
1 synchronized (queue)
2 queueSize == 0, queue.wait(); queue.notify();
queue.poll(); queue.notify();
生产者:
1 synchronized (queue)
2 queueSize==queueSize, queue.wait();queue.notify();
3 queue.offer(1); queue.notify();

// 非阻塞队列实现 生产者-消费者模式
public class NotBlockedProducerConsumer {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);

    public static void main(String[] args) {
        NotBlockedProducerConsumer test = new NotBlockedProducerConsumer();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();

        producer.start();
        consumer.start();

    }

    class Consumer extends Thread{
        public void run(){
            consume();
        }

        private void consume() {
            while (true){
                synchronized (queue){
                    while (queueSize == 0){
                        try{
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        }catch (InterruptedException e){
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.poll(); //每次移走队首元素
                    queue.notify();
                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
                }
            }
        }
    }

    class Producer extends Thread{
        public void run(){
            produce();
        }

        private void produce() {
            while(true){
                synchronized (queue){
                    while (queueSize==queueSize){
                        try{
                            System.out.println("队列满,等待有空余空间");
                            queue.wait();
                        }catch (InterruptedException e){
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.offer(1); //每次插入一个元素
                    queue.notify();
                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
                }
            }
        }
    }
}
  1. 使用阻塞队列实现 生产者-消费者模式

代码要点:
1 定义一个阻塞队列
private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize);
2 生产者 放:queue.put(1);
3 消费者 取:queue.take();

// 阻塞队列 实现的 生产者-消费者模式
public class BlockedProductConsumer {
    private int queueSize = 10;
    private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize);

    public static void main(String[] args) {
        BlockedProductConsumer test = new BlockedProductConsumer();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();

        producer.start();
        consumer.start();

    }

    class Consumer extends Thread{
        public void run(){
            consume();
        }

        private void consume() {
            while (true){
                try{
                    queue.take();
                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Producer extends Thread{
        public void run(){
            produce();
        }

        private void produce() {
            while (true){
                try{
                    queue.put(1);
                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值