Java中的阻塞队列

83 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

阻塞队列是支持阻塞的插入和移除的队列,即在队列满时,阻塞插入元素的线程直到队列不满,在队列空时,阻塞获取元素的线程直到队列非空。Java中有7种阻塞队列

有界队列

数组组成的有界队列:ArrayBlockingQueue

底层是数组的严格按照先进先出逻辑的阻塞队列,其构造方法包含两个参数

ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(100, true);

第一个参数为capacity,即队列的容量,必填;第二个参数为是否保证线程公平,即先阻塞的线程一定在满足条件时一定先执行操作,默认为false

链表组成的有界队列:LinkedBlockingQueue

底层是链表的严格按照先进先出逻辑的阻塞队列

LinkedBlockingQueue queue = new LinkedBlockingQueue<Integer>();

由于底层是链表,无需像 ArrayBlockingQueue 一样传入capacity 初始化底层数组,因而容量不是必填的,默认为Integer.MAX_VALUE

链表组成的双向队列:LinkedBlockingDeque

底层是链表的双向队列

无界队列

无界队列是能够自动扩容的队列

支持优先级排序的无界队列:PriorityBlockingQueue

默认情况下根据元素的 compareTo() 方法来指定排序规则,或者在初始化时传入自定义的Comparator来进行排,它的底层数据结构也同样为数组,默认大小为Integer.MAX_VALUE。

PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(100, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            //
        }
    });

使用优先级队列实现的支持延时获取的无界队列:DelayQueue

ScheduleThreadPoolExecutor 就是使用了 DelayQueue 实现的定时调度线程池,线程任务都被包装为 ScheduledFutureTask ,参考 ScheduledFutureTask 的实现就可知道如何使用 DelayQueue ,即入队的元素必须实现 Delayed 接口实现 getDelay() 方法,该方法指定延时,推荐使用纳秒(1s=109ns1s=10^9ns),延时结束后才能线程获取元素,否则会阻塞。另外还需要实现 compareTo() 方法来决定元素在队列中的优先级。

public class DelayElement implements Delayed {

    private final long time;

    public DelayElement(long time) {
        this.time = time;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(time - System.currentTimeMillis(), TimeUnit.NANOSECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return 0;
    }
}

public class Main {

    public static void main(String[] args) throws InterruptedException {
        DelayQueue<DelayElement> queue = new DelayQueue<>();
        queue.put(new DelayElement(DateTime.now().plusHours(1).getMillis()));
        DelayElement take = queue.take(); // 方法阻塞直到一小时后到了为止
    }
}

链表组成的支持transfer的无界队列:LinkedTransferQueue

LinkedTransferQueue 和其他队列的不同点是增加了 transfer()方法和 tryTransfer() 方法,用于是跟踪元素被消费的情况。调用transfer方法时,如果有消费者正在等待消费,那么元素直接transfer给消费者,反之放到队尾,直到元素被消费了,该方法才会返回。tryTransfer() 方法是立即返回,如果元素理解被消费返回true,否则返回false,另外可以自定义 tryTransfer() 方法的超时时间。

queue.tryTransfer(1L, 10, TimeUnit.MILLISECONDS);

不存储元素的队列

不存储元素的队列:SynchronousQueue

一个不存储元素的阻塞队列,即每一个put操作都必须等待一个take操作,队列本身不做任何的存储,只有在生产者和消费者都有请求时,起到一个传递的作用。