前言
-
LinkedTransferQueue是JDK1.7才添加的阻塞队列,基于链表实现的FIFO无界阻塞队列,是ConcurrentLinkedQueue(循环CAS+volatile 实现的wait-free并发算法)、SynchronousQueue(公平模式下转交元素)、LinkedBlockingQueue(阻塞Queue的基本方法)的超集。而且LinkedTransferQueue更好用,因为它不仅仅综合了这几个类的功能,同时也提供了更高效的实现。
-
BlockingQueue接口,它是指这样的一个队列:当生产者向队列添加元素但队列已满时,生产者会被阻塞;当消费者从队列移除元素但队列为空时,消费者会被阻塞。
-
LinkedTransferQueue采用的一种预占模式。意思就是消费者线程取元素时,如果队列为空,那就生成一个节点(节点元素为null)入队,然后消费者线程被等待在这个节点上,后面生产者线程入队时发现有一个元素为null的节点,生产者线程就不入队了,直接就将元素填充到该节点,唤醒该节点等待的线程,被唤醒的消费者线程取走元素,从调用的方法返回。即找到匹配的节点不入队,找不到根据how参数入队。
TransferQueue接口
TransferQueue继承自BlockingQueue接口
LinkedTransferQueue定义
实现了TransferQueue接口
LinkedTransferQueue链表节点Node
将没有匹配的数据节点进行匹配,匹配成功后设置item为null,队列中占位,然后阻塞等待在此节点上的消费者,等待被唤醒被唤醒则返回true。
匹配前后节点item的变化,其中node1为数据节点,node2是消费者请求的占位节点
- 数据节点,则匹配前item不为null且不为自身,匹配后设置为null。
- 占位请求节点,匹配前item为null,匹配后自连接。
LinkedTransferQueue重要字段
head和tail节点初始化时都为null
构造器
LinkedTransferQueue是无界队列,并没有指定容量的构造器
xfer核心方法
实现所有队列的出队入队方法的通用方法
how 参数
xfer方法
简单说一下xfer方法的流程
- 寻找和操作匹配的节点
- 从head开始向后遍历寻找未被匹配的节点,找到一个未被匹配的节点再判断操作和节点类型是否匹配(put和占位请求节点匹配,take和数据节点匹配),如果不匹配则跳出内循环再从head开始寻找未匹配节点(重复),否则通过CAS设置匹配节点的item为e,设置成功后再CAS设置新的head节点为匹配节点的后继节点,向后推进head,将原来的head节点自连接,等待被GC,然后唤醒阻塞在这个节点上的线程,并返回匹配节点的item元素,元素没有入队,而是直接从生产者转交给消费者。
- 上面可分两个步骤:找到未被匹配的节点,然后对节点的类型进行判断和设置head
- put和take的不同:put本身带有data(e != null),put找到匹配的节点,如果节点类型也满足(not isData),则将e设置为p的item元素,并向后推进head,然后唤醒等待的线程,并返回匹配节点原来的item(null);take本身不带data(e == null),take找到匹配的节点,如果节点类型也满足( isData),则将e设置为p的item元素(item变为null),并向后推进head,然后唤醒等待的线程,并返回匹配节点原来的item(not null)。
- 如果遍历整个队列都没有匹配的节点,则执行相对应的how操作处理
注意如果isData为false,则是将一个占位请求节点插入队列尾部。
- NOW:立即返回,也不会插入节点
- SYNC:插入一个item为e(isData = haveData)到队列的尾部,然后自旋或阻塞当前线程直到节点被匹配或者取消。
- ASYNC:插入一个item为e(isData = haveData)到队列的尾部,不阻塞直接返回。
- TIMED:插入一个item为e(isData = haveData)到队列的尾部,然后自旋或阻塞当前线程直到节点被匹配或者取消或者超时。
- 阻塞或者自旋
插入的占位节点自旋超过一个阀值时将阻塞线程,当频繁写操作时避免过度自旋消耗CPU
ASYNC操作(put、offer、add)
how 参数为ASYNC,即找不到匹配节点,则入队不等待直接返回
NOW操作(poll、tryTransfer)
how 参数为NOW,即找不到匹配节点,则将构建新节点入队并立即返回
SYNC操作(transfer、take)
how 参数为SYNC,即找不到匹配节点,则将构建新节点入队并阻塞线程直到节点被匹配或被取消
TIMED操作(poll、tryTransfer)
how 参数为TIMED,即找不到匹配节点,则将构建新节点入队并阻塞线程直到节点被匹配或被取消或者超时
用途与注意事项
-
使用LinkedTransferQueue来代替SynchronousQueue也会使得ThreadPoolExecutor得到相应的性能提升。
-
尽量避免使用how == SYNC的操作,因为LinkedTransferQueue是无界队列,当大量线程都阻塞,则会占用大量的线程资源。
网友评论