SynchronousQueue介绍
【1】SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take。
【2】如图所示,SynchronousQueue 最大的不同之处在于,它的容量为 0,所以没有一个地方来暂存元素,导致每次取数据都要先阻塞,直到有数据被放入;同理,每次放数据的时候也会阻塞,直到有消费者来取。
【3】需要注意的是,SynchronousQueue 的容量不是 1 而是 0,因为 SynchronousQueue 不需要去持有元素,它所做的就是直接传递(direct handoff)。由于每当需要传递的时候,SynchronousQueue 会把元素直接从生产者传给消费者,在此期间并不需要做存储,所以如果运用得当,它的效率是很高的。
SynchronousQueue的源码分析
【1】构造函数
【2】核心方法分析
【1】Transferer是SynchronousQueue的内部抽象类,双栈和双队列算法共享该类。他只有一个transfer方法,用于转移元素,从生产者转移到消费者;或者消费者调用该方法从生产者取数据。
【2】Transferer有两个实现类:TransferQueue和TransferStack。
【3】这两个类的区别就在于是否公平。TransferQueue是公平的,TransferStack非公平。
【4】源码展示
【1】节点元素
【2】构造方法
【3】核心方法
【1】节点元素
【2】核心方法
SynchronousQueue总结
【1】是一个没有数据缓冲的BlockingQueue,容量为0,它不会为队列中元素维护存储空间,它只是多个线程之间数据交换的媒介。
【2】数据结构:链表,在其内部类中维护了数据
先消费(take),后生产(put);
第一个线程Thread0是消费者访问,此时队列为空,则入队(创建Node结点并赋值)
第二个线程Thread1也是消费者访问,与队尾模式相同,继续入队
第三个线程Thread2是生产者,携带了数据e,与队尾模式不同,不进行入队操作。直接将该线程携带的数据e返回给队首的消费者,并唤醒队首线程Thread1(默认非公平策略是栈结构),出队。
反之,先生产(put)后消费(take),原理一样
【3】锁:CAS+自旋(无锁)【阻塞:自旋了一定次数后调用 LockSupport.park()】
【4】存取调用同一个方法:transfer()
put、offer 为生产者,携带了数据 e,为 Data 模式,设置到 SNode或QNode 属性中。
take、poll 为消费者,不携帯数据,为 Request 模式,设置到 SNode或QNode属性中。
【5】过程
线程访问阻塞队列,先判断队尾节点或者栈顶节点的 Node 与当前入队模式是否相同
相同则构造节点 Node 入队,并阻塞当前线程,元素 e 和线程赋值给 Node 属性
不同则将元素 e(不为 null) 返回给取数据线程,队首或栈顶线程被唤醒,出队
【6】公平模式:TransferQueue,队尾匹配(判断模式),队头出队,先进先出
【7】非公平模式(默认策略):TransferStack,栈顶匹配,栈顶出栈,后进先出
【8】应用场景
SynchronousQueue非常适合传递性场景做交换工作,生产者的线程和消费者的线程同步传递某些信息、事件或者任务。
SynchronousQueue的一个使用场景是在线程池里。如果我们不确定来自生产者请求数量,但是这些请求需要很快的处理掉,那么配合SynchronousQueue为每个生产者请求分配一个消费线程是处理效率最高的办法。Executors.newCachedThreadPool()就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。
Original: https://www.cnblogs.com/chafry/p/16782932.html
Author: 忧愁的chafry
Title: SynchronousQueue详解
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/711744/
转载文章受原作者版权保护。转载请注明原作者出处!