33 高级阻塞队列之SynchronousQueue、LinkedTransferBlockingQueue
Diego38 54 1

1. 前言

前面我们学习了阻塞队列的两种常见队列,除了通过数组和链表实现的阻塞队列,还有很多其他的阻塞队列,这些阻塞队列都有各自的特点,适用于一些特殊场景,比如SynchronousQueue内部是无容量的队列,而LinkedTransferQueue省却了加锁过程,是阻塞队列中性能最优的无界队列。

本节我们就一起学习这两种队列。

2. SynchronousQueue的使用场景和实现

SynchronousQueue是一个不存储任何元素的阻塞队列。它更像是一个传送装置,put和take两个线程要成对出现才能完成一次传送。

看下面的例子:

    public class SynchronousQueueTest {

    static final SynchronousQueue<String> queue = new SynchronousQueue<>();

    static Thread putThread = new Thread(() -> {
        try {
            queue.put("hello world");
        } catch (InterruptedException e) {
        }
    });

    static Thread takeThread = new Thread(() ->  {
            try {
                System.out.println("take element : " + queue.take());
            } catch (InterruptedException e) {
            }
    });

    public static void main(String[] args) throws InterruptedException {
        putThread.start();
        takeThread.start();
    }
}

输出

take element : hello world

出队列线程和入队列线程要成对出现,一个线程的释放需要另外一个线程unpark。当有一个put线程在做put操作时,需要等待另一个take线程获取,同样take线程先进行获取时,会等待一个put线程唤醒take线程。

由此可见,我们在操作SynchronousQueue队列时,一般会使用阻塞方法比如take、poll(time, unit)、put、offer(time, unit), 纯粹的不超时方法poll和offer,是很难配对成功的。 image

synchronousQueue实际上相当于一个管道,每次入队列线程都需要一个出队列的线程,反之亦然。同一线程则不可。第二张图表示synchronousQueue里面的队列情况,就像祖玛游戏一样,只不过祖玛是连续三个以上彩球,synchronousQueue是两个对立mode的操作遇到一起消失。

SynchronousQueue在Executors.newCachedThreadPool中会用到,用于构造无数量线程的线程池。

SynchronousQueue是无锁队列,拥有比ArrayBlockingQueue和LinkedBlockingQueue更高的性能,但是缺点也很明显,内部无容量,很容易出现阻塞。

3. LinkedTransferQueue的使用场景和实现

LinkedTransferQueue是一个基于链表实现的无界阻塞队列,LinkedTransferQueue 最大的特性是它的结点中既可以存放线程也可以存放具体元素。

LinkedTransferQueue是JDK1.7新引入的无锁队列,相对ArrayBlockingQueue和LinkedBlockingQueue性能更优,唯一的缺点是只能支持无界,不支持有界。

它是怎么做到无锁化的呢,分析下结构我们就能看出一二。 image

LinkedTransferQueue实现无锁的核心关键是take线程(或者poll超时线程)可以把当前的TransferQueue作为等待队列进行入队,由于队列是先进先出的,而线程的唤醒也是先进先出的,这使得这一设计成为可能。

当队列中没有任何元素时,take线程会形成一条等待出队的线程链表,当有put线程进来时,和synchronousQueue类似,与排在头结点的head线程进行匹配,匹配成功两线程同时返回,take线程得到了数据,put线程执行了返回。

因此LinkedTransferQueue只会出现两种情况的队列,要么队列中全部都是具体的元素,要么队列是排队等待put线程的take线程链表。

得益于LinkedTransferQueue的无界,put线程不需要进入队列满的阻塞。

无界队列中,LinkedTransferQueue是最优的队列:

  • 优点: 性能好,无锁,又能支持阻塞
  • 缺点: 只支持无界 无界队列省去了队列满的条件阻塞,却也失去了很多场景的适用性,比如线程池内部缓冲队列时,使用无界队列会使得任务数无线增加,最终造成OOM。

4. 总结

image image

预览图
评论区

索引目录