阻塞队列ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue

Queue队列

队列FIFO, 先进先出。

Queue接口抽象出了以下几个方法, 我们可以实现Queue接口,定制贴合自己业务的队列

boolean add(E e)

添加元素容量不足抛异常

boolean offer(E e)

添加元素容量不足返回false

E remove()

删除头元素队列为空抛异常

E poll()

删除头元素队列为空返回null

E element()

检索但并不删除头元素队列为空抛异常

E peek()

检索但并不删除头元素队列为空返回null

阻塞队列

队列

描述

ArrayBlockingQueue

基于数组结构实现的一个有界阻塞队列

LinkedBlockingQueue

基于链表结构实现的一个无界阻塞队列,指定容量为有界阻塞队列

PriorityBlockingQueue

支持按优先级排序的无界阻塞队列

DelayQueue

基于优先级队列(PriorityBlockingQueue)实现的无界阻塞队列

SynchronousQueue

不存储元素的阻塞队列

LinkedTransferQueue

基于链表结构实现的一个无界阻塞队列

LinkedBlockingDeque

基于链表结构实现的一个双端阻塞队列

ArrayBlockingQueue

基于环形数组实现的有界阻塞队列,利用ReentrantLock实现线程安全, 入队和出队公用一把锁, 可能导致并发性能问题

结构:

  1. 有界环形数组(初始化时指定数组容量, 不能扩容)
  2. 一把ReentrantLock锁
  3. 两个Condition条件(notEmpty, notFull)
  4. 两个指针标识入队和出队元素在数组下标 

LinkedBlockingQueue

基于链表实现的阻塞队列, 不指定容量时默认Integer.MAX_VALUE(2的31次方-1), 因为容量很大, 也常被称作无界阻塞队列.在生产消费场景中, 如果生产速度大于消费速度, 可能导致OOM. 建议使用时, 测试出生产消费速度, 设置队列容量, 防止OOM.

结构:

  1. 链表, 有头节点和尾节点
  2. 两把ReentrantLock锁, 入队锁putLock和出队锁takeLock
  3. 两个Condition条件(notEmpty, notFull)

PriorityBlockingQueue

基于PriorityQueue实现的阻塞队列, 有初始容量11, 会自动扩容(50%), 入队和出队共用一把锁

示例: vip用户插队

@Slf4j
public class PriorityDemo {

    private static PriorityBlockingQueue<User> blockingQueue = new PriorityBlockingQueue<>();

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            produce(new User("小" + i, i % 5 == 0));
        }
        blockingQueue.offer(new User(null));
        new Thread(PriorityDemo::comsume).start();
    }

    private static void produce(User user) {
        blockingQueue.offer(user);
        log.info("开始排队 " + user.getDesc());
    }

    private static void comsume() {
        User user;
        try {
            while ((user = blockingQueue.take()).getName() != null) {
                log.info("开始叫号, " + user.getDesc() + " 请进...");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Data
    static class User implements Comparable<User> {

        private String name;

        private boolean isVip;

        private String getDesc() {
            if (name == null) return null;
            return (isVip ? "VIP" : "平民") + "[" + name + "]";
        }

        public User(String name) {
            this.name = name;
        }

        public User(String name, boolean isVip) {
            this.name = name;
            this.isVip = isVip;
        }

        @Override
        public int compareTo(User o) {
            if (o.getName() == null) {
                return -1;
            }
            if (name == null) {
                return 1;
            }
            return o.isVip ? 1 : -1;
        }
    }
}

 执行结果:

[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小1]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小2]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小3]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小4]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 VIP[小5]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小6]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小7]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小8]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 平民[小9]
[main][INFO][23/10/24]-org.example.queue.PriorityDemo.produce(PriorityDemo.java:31):开始排队 VIP[小10]
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, VIP[小5] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, VIP[小10] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小3] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小4] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小8] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小6] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小2] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小9] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小1] 请进...
[Thread-0][INFO][23/10/24]-org.example.queue.PriorityDemo.comsume(PriorityDemo.java:38):开始叫号, 平民[小7] 请进...

 

如何选择队列

1. 功能. 若业务需要延时, 优先级. 排序, 可考虑用PriorityBlockingQueue之类的阻塞队列

2. 容量. 判断当前是否需要考虑内存成本, 来判断选用有界/无界/无容量队列(SynchronousQueue)

3. 能否扩容. 若不能判断高峰业务具体容量大小, 应支持动态扩容, 不能用ArrayBlockingQueue

4. 内存结构. ArrayBlockingQueue内部结构是数组, 需要一块连续内存空间; LinkedBlockingQueue是一个链表, 带有Node结构, ArrayBlockingQueue没有链表Node节点, 空间利用率更高

5. 性能. ArrayBlockingQueue和PriorityBlockingQueue入队出队共用一把锁, LinkedBlockingQueue有两把锁, LinkedBlockingQueue并发性能更高.

6. 是否直接传递. 如果直接传递用SynchronousQueue, 不需要额外空间把任务放到队列中, 节省内存

线程池中的队列

  • FixedThreadPool: LinkedBlockingQueue
  • CachedThreadPool: SynchronousQueue
  • ScheduledThreadPool: DelayQueue
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值