【线程池】常用的三种阻塞队列

邓敏 2021年12月17日 134次浏览

简介

我们在了解完线程池的参数配置常用线程池后发现,每种线程池会根据不同的需求去选择不同的队列来存储线程任务。线程池的对应队列如下:
image.png

可以看到,五大常用的线程池,会用到三种线程池

LinkedBlockingQueue

LinkedBlockingQueue是一种没有容量上限的队列,也就是说,用了这个队列的线程池,就可以没有上限的去保存队列任务。这种需求场景就很符合FixedThreadPool和SingleThreadExecutor,这两种线程池都有一个相同点,那就是核心线程数和最大线程数是一致的,线程数都固定了,当任务多的时候线程处理不过来的线程就会放到队列中,这时就需要一个没有上限的队列来存储线程池的任务了。

SynchronousQueue

SynchronousQueue内部结构中没有存储队列的容器,因此没有办法去存储队列消息,那么就会有人想,不能存储消息那算哪门子队列是吧,其实队列不一定需要会存储消息,SynchronousQueue虽然不能存储队列消息,但是他可以阻塞队列消息,可以很好的完成中转消息的用处,接下来我们可以参考下面这段代码

public class SynchronousQueueDemo {

    public static void main(String[] args) {
        SynchronousQueue<String> queue = new SynchronousQueue<>();

        Thread producer = new Thread(()->{
            int i = 0;
            while (true){
                System.out.println("生产者开始生产消息...");
                String msg = "这是第"+(++i)+"条消息";
                try {
                    int seconds = getRandomSeconds();
                    System.out.println("生产者生产消息花费了"+seconds+"秒");
                    TimeUnit.SECONDS.sleep(seconds);
                    queue.put(msg);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产者消息生产完毕...");
            }
        });

        Thread consumer = new Thread(()->{
            while (true){
                System.out.println("消费者开始消费消息...");
                try {
                    System.out.println("消费者消费到消息:"+queue.take());
                    int seconds = getRandomSeconds();
                    System.out.println("消费者消费消息花费了"+seconds+"秒");
                    TimeUnit.SECONDS.sleep(seconds);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();
        consumer.start();
    }

    private static int getRandomSeconds(){
        Random random = new Random();
        return random.nextInt(10)+1;
    }
}

生产者会随机1到10秒去生产消息,消费者也会随机1到10秒去消费消息,如果当遇到生产者正在生产消息的时候,消费者端就会因为没有消息消费而阻塞在那里,同理,如果消费者正在消费消息,生产者也会因为队列没法存储消息而阻塞在那里。
像这种场景就很适合CachedThreadPool这样的线程池,因为CachedThreadPool可以创建的线程数是无限的,也就是说在这个线程池里面,任务队列能不能存储消息其实已经变的可有可无了,但是为了提高线程队列中转消息的性能,SynchronousQueue就变的更加合适,因为他想对于LinkedBlockingQueue,SynchronousQueue少了去存储消息的性能消耗,自然的性能就增加了。

DelayedWorkQueue

DelayedWorkQueue的数据结构是采用数组来实现堆,并且内部元素并不是按照放入的时间顺序来排序的,而是会按照延迟的时间长短对任务进行排序。我们可以看到ScheduledThreadPool和SingleThreadScheduledExecutor都适用DelayedWorkQueue来存放队列,其实就是因为他可以按延迟时间长短排序任务执行的特性,来实现线程池定时的功能。