阻塞队列和非阻塞队列

Java阻塞队列和非阻塞队列

在这里插入图片描述

1. 什么是阻塞队列?

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列

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

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

JDK7 提供了 7 个阻塞队列。分别是:

  • ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。

  • LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。

  • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。

  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。

  • SynchronousQueue:一个不存储元素的阻塞队列。

  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

Java 5 之前实现同步存取时,可以使用普通的一个集合,然后在使用线程的协作和线程同步可以实现生产者,消费者模式,主要的技术就是用好,wait,notify,notifyAll,sychronized 这些关键字。而在 java 5 之后,可以使用阻塞队列来实现,此方式大大简少了代码量,使得多线程编程更加容易,安全方面也有保障。

BlockingQueue 接口是 Queue 的子接口,它的主要用途并不是作为容器,而是作为线程同步的的工具,因此他具有一个很明显的特性,当生产者线程试图向 BlockingQueue 放入元素时,如果队列已满,则线程被阻塞,当消费者线程试图从中取出一个元素时,如果队列为空,则该线程会被阻塞,正是因为它所具有这个特性,所以在程序中多个线程交替向 BlockingQueue 中放入元素,取出元素,它可以很好的控制线程之间的通信。

阻塞队列使用最经典的场景就是 socket 客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析

2. 阻塞队列的生产者-消费者模式

阻塞队列(Blocking queue)提供了可阻塞的put和take方法,它们与可定时的offer和poll是等价的。如果Queue已经满了,put方法会被阻塞直到有空间可用;如果Queue是空的,那么take方法会被阻塞,直到有元素可用。Queue的长度可以有限,也可以无限;无限的Queue永远不会充满,所以它的put方法永远不会阻塞

阻塞队列支持生产者-消费者设计模式。一个生产者-消费者设计分离了“生产产品”和“消费产品”。该模式不会发现一个工作便立即处理,而是把工作置于一个任务(“to do”)清单中,以备后期处理。生产者-消费者模式简化了开发,因为它解除了生产者和消费者之间相互依赖的代码。生产者和消费者以不同的或者变化的速度生产和消费数据,生产者-消费者模式将这些活动解耦,因而简化了工作负荷的管理。

生产者-消费者设计是围绕阻塞队列展开的,生产者把数据放入队列,并使数据可用,当消费者为适当的行为做准备时会从队列中获取数据。生产者只负责把数据放入队列。类似地,消费者也不需要知道生产者是谁,以及是谁给它们安排的工作。BlockingQueue可以使用任意数量的生产者和消费者,从而简化了生产者-消费者设计的实现。最常见的生产者-消费者设计是将线程池与工作队列相结合

3. 有哪些非阻塞队列?

  • ArrayQueue:数组队列
  • ArrayDeque:数组双端队列
  • PriorityQueue:优先级队列
  • ConcurrentLinkedQueue:基于链表的并发队列

4. 使用阻塞队列和非阻塞队列的生产者-消费者模式代码

场景:1个生产者线程,3个消费者线程,工作队列的大小为2

4.1 阻塞队列(LinkedBlockingQueue实现)
package com.feng.queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created by FengBin on 2021/8/17 14:20
 * 基于阻塞队列实现的生产者-消费者模式
 */
public class TestBlockingQueue {
    private int queueSize = 2;   //生产者的队列大小
    private BlockingQueue<Integer> workQueue = new LinkedBlockingQueue<Integer>(queueSize);

    public static void main(String[] args) throws InterruptedException {
        TestBlockingQueue solution = new TestBlockingQueue();
        Producer producer1 = solution.new Producer();
        Consumer consumer1 = solution.new Consumer();
        Consumer consumer2 = solution.new Consumer();
        Consumer consumer3 = solution.new Consumer();

        producer1.start();
        consumer1.start();
        consumer2.start();
        consumer3.start();
    }


    //定义一个生产者
    class Producer extends Thread {
        @Override
        public void run() {
            produce();
        }

        private void produce() {
            String name = Thread.currentThread().getName() + "-producer";
            while (true) {
                try {
                    Thread.sleep(2000);
                    workQueue.put(1);
                    System.out.println("[" + name + "]" + "向队列中生产一个元素,队列的剩余空间为:" + (queueSize - workQueue.size()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //定义一个消费者
    class Consumer extends Thread {
        @Override
        public void run() {
            consume();
        }

        public void  consume() {
            String name = Thread.currentThread().getName() + "-consumer";
            while (true) {
                try {
                    System.out.println("[" + name + "]" + "准备从队列中消费元素");
                    workQueue.take();
                    System.out.println("[" + name + "]" + "从队列中消费一个元素,队列的剩余空间为:" + (queueSize - workQueue.size()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

输出结果:

[Thread-2-consumer]准备从队列中消费元素
[Thread-1-consumer]准备从队列中消费元素
[Thread-3-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-2-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-2-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-1-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-1-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-3-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-3-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-2-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-2-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-1-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-1-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-3-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-3-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-2-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-2-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-1-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-1-consumer]准备从队列中消费元素
[Thread-0-producer]向队列中生产一个元素,队列的剩余空间为:1
[Thread-3-consumer]从队列中消费一个元素,队列的剩余空间为:2
[Thread-3-consumer]准备从队列中消费元素

可以看出,只有当队列中存在元素,消费者线程才会去队列中取元素,否则将会阻塞,直到队列中生产了元素以后才会继续执行

4.2 非阻塞队列(ConcurrentLinkedQueue实现)
package com.feng.queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Created by FengBin on 2021/8/17 14:47
 */
public class TestConcurrentLinkedQueue {
    private ConcurrentLinkedQueue<Integer> workQueue = new ConcurrentLinkedQueue<>();

    public static void main(String[] args) {
        TestConcurrentLinkedQueue solution = new TestConcurrentLinkedQueue();
        Producer producer1 = solution.new Producer();
        Consumer consumer1 = solution.new Consumer();
        Consumer consumer2 = solution.new Consumer();
        Consumer consumer3 = solution.new Consumer();
        producer1.start();
        consumer1.start();
        consumer2.start();
        consumer3.start();

    }

    //定义一个生产者
    class Producer extends Thread {
        @Override
        public void run() {
            produce();
        }

        private void produce() {
            String name = Thread.currentThread().getName() + "-producer";
            while (true) {
                try {
                    Thread.sleep(2000);
                    workQueue.offer(1);
                    System.out.println("[" + name + "]" + "向队列中生产一个元素");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //定义一个消费者
    class Consumer extends Thread {
        @Override
        public void run() {
            consume();
        }

        public void  consume() {
            String name = Thread.currentThread().getName() + "-consumer";
            while (true) {
                try {
                    Thread.sleep(1000);
                    System.out.println("[" + name + "]" + "准备从队列中消费元素");
                    Integer poll = workQueue.poll();
                    System.out.println("[" + name + "]" + "从队列中消费元素:" + poll);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出结果:

[Thread-2-consumer]准备从队列中消费元素
[Thread-3-consumer]准备从队列中消费元素
[Thread-2-consumer]从队列中消费元素:null
[Thread-1-consumer]准备从队列中消费元素
[Thread-3-consumer]从队列中消费元素:null
[Thread-1-consumer]从队列中消费元素:null
[Thread-0-producer]向队列中生产一个元素
[Thread-2-consumer]准备从队列中消费元素
[Thread-1-consumer]准备从队列中消费元素
[Thread-1-consumer]从队列中消费元素:null
[Thread-3-consumer]准备从队列中消费元素
[Thread-3-consumer]从队列中消费元素:null
[Thread-2-consumer]从队列中消费元素:1
[Thread-2-consumer]准备从队列中消费元素
[Thread-2-consumer]从队列中消费元素:null
[Thread-3-consumer]准备从队列中消费元素
[Thread-3-consumer]从队列中消费元素:null
[Thread-1-consumer]准备从队列中消费元素
[Thread-1-consumer]从队列中消费元素:null
[Thread-0-producer]向队列中生产一个元素
[Thread-1-consumer]准备从队列中消费元素
[Thread-1-consumer]从队列中消费元素:1
...

使用非阻塞队列,当消费者线程从队列中获取元素,而此时队列为空,则不会阻塞,而是返回null,即获取到null

如果使用ConcurrentLinkedQueue作为生产者-消费者模型的队列使用,应该加上锁的并发控制机制

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值