并发集合9-LinkedTransferQueue

前言

  1. LinkedTransferQueue是JDK1.7才添加的阻塞队列,基于链表实现的FIFO无界阻塞队列,是ConcurrentLinkedQueue(循环CAS+volatile 实现的wait-free并发算法)SynchronousQueue(公平模式下转交元素)LinkedBlockingQueue(阻塞Queue的基本方法)的超集。而且LinkedTransferQueue更好用,因为它不仅仅综合了这几个类的功能,同时也提供了更高效的实现

  2. BlockingQueue接口,它是指这样的一个队列:当生产者向队列添加元素但队列已满时,生产者会被阻塞;当消费者从队列移除元素但队列为空时,消费者会被阻塞

  3. LinkedTransferQueue采用的一种预占模式。意思就是消费者线程取元素时,如果队列为空,那就生成一个节点(节点元素为null)入队,然后消费者线程被等待在这个节点上,后面生产者线程入队时发现有一个元素为null的节点,生产者线程就不入队了,直接就将元素填充到该节点唤醒该节点等待的线程,被唤醒的消费者线程取走元素,从调用的方法返回。即找到匹配的节点不入队,找不到根据how参数入队。

TransferQueue接口

并发集合9-LinkedTransferQueue_第1张图片

TransferQueue继承自BlockingQueue接口

LinkedTransferQueue定义


并发集合9-LinkedTransferQueue_第2张图片

实现了TransferQueue接口

LinkedTransferQueue链表节点Node

并发集合9-LinkedTransferQueue_第3张图片

并发集合9-LinkedTransferQueue_第4张图片

将没有匹配的数据节点进行匹配,匹配成功后设置item为null,队列中占位,然后阻塞等待在此节点上的消费者,等待被唤醒被唤醒则返回true。


并发集合9-LinkedTransferQueue_第5张图片

匹配前后节点item的变化,其中node1为数据节点,node2是消费者请求的占位节点

  1. 数据节点,则匹配前item不为null且不为自身,匹配后设置为null。
  2. 占位请求节点,匹配前item为null,匹配后自连接。

LinkedTransferQueue重要字段

并发集合9-LinkedTransferQueue_第6张图片

head和tail节点初始化时都为null


并发集合9-LinkedTransferQueue_第7张图片

构造器

并发集合9-LinkedTransferQueue_第8张图片

LinkedTransferQueue是无界队列,并没有指定容量的构造器

xfer核心方法

实现所有队列的出队入队方法的通用方法

how 参数


并发集合9-LinkedTransferQueue_第9张图片

xfer方法


并发集合9-LinkedTransferQueue_第10张图片

并发集合9-LinkedTransferQueue_第11张图片

简单说一下xfer方法的流程

  1. 寻找和操作匹配的节点
  • 从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)。
  1. 如果遍历整个队列都没有匹配的节点,则执行相对应的how操作处理

注意如果isData为false,则是将一个占位请求节点插入队列尾部。

  • NOW:立即返回,也不会插入节点
  • SYNC:插入一个item为e(isData = haveData)到队列的尾部,然后自旋或阻塞当前线程直到节点被匹配或者取消。
  • ASYNC:插入一个item为e(isData = haveData)到队列的尾部,不阻塞直接返回。
  • TIMED:插入一个item为e(isData = haveData)到队列的尾部,然后自旋或阻塞当前线程直到节点被匹配或者取消或者超时
  1. 阻塞或者自旋

插入的占位节点自旋超过一个阀值时将阻塞线程,当频繁写操作时避免过度自旋消耗CPU

ASYNC操作(put、offer、add)

how 参数为ASYNC,即找不到匹配节点,则入队不等待直接返回


并发集合9-LinkedTransferQueue_第12张图片

NOW操作(poll、tryTransfer)

how 参数为NOW,即找不到匹配节点,则将构建新节点入队并立即返回


并发集合9-LinkedTransferQueue_第13张图片

SYNC操作(transfer、take)

how 参数为SYNC,即找不到匹配节点,则将构建新节点入队并阻塞线程直到节点被匹配或被取消


并发集合9-LinkedTransferQueue_第14张图片

TIMED操作(poll、tryTransfer)

how 参数为TIMED,即找不到匹配节点,则将构建新节点入队并阻塞线程直到节点被匹配或被取消或者超时


并发集合9-LinkedTransferQueue_第15张图片

用途与注意事项

  1. 使用LinkedTransferQueue来代替SynchronousQueue也会使得ThreadPoolExecutor得到相应的性能提升

  2. 尽量避免使用how == SYNC的操作,因为LinkedTransferQueue是无界队列当大量线程都阻塞,则会占用大量的线程资源

你可能感兴趣的:(并发集合9-LinkedTransferQueue)