关于线程间传递数据的队列,SynchronousQueue队列
是一个支持公平和非公平的线程安全的队列
对于在HikariCP中,其对应的实现是选择了对应的公平模式,
this.handoffQueue = new SynchronousQueue<>(true)
内部维护的是一个无存储空间的阻塞队列,生产者和消费者之间的线程可以线程同步的传递某些信息或者时间,也就是说一个读操作需要一个写操作,缺少一个就会阻塞线程,直到匹配位置
基于了队列和栈来实现,双队列提供了公平模式,双栈则是非公平模式
其内部的字段主要有个
1.NCPUS:代表CPU的数量
2.maxTimedSpins:自旋的次数,如果指定了timeout的事件,则使用maxTimeSpins,如果CPU小于2则自旋次数为0,一个常量而已
3.maxUntimedSpins:自选测试,同样一个常量
4.自定义时间:防止自定义时间过长的保护常量罢了
一个生产线程,当它生产产品(即put的时候),如果当前没有人想要消费产品(即当前没有线程执行take),此生产线程必须阻塞,等待一个消费线程调用take操作,take操作将会唤醒该生产线程,同时消费线程会获取生产线程的产品(即数据传递),这样的一个过程称为一次配对过程(当然也可以先take后put,原理是一样的)
关于其中的公平队列,采用了双队列的模式,进行互补的等待,如果模式相同,就进行等待,互补再进行消费
那么是如何存入和消费的呢?
如果是公平模型下的存入和消费
底层实现使用的是TransferQueue这个内部队列,它有一个head和tail指针,用于指向当前正在等待匹配的线程节点。
初始化时,TransferQueue的状态如下:
接下来入队两个put请求,分别自旋后睡眠
然后来了一个线程进行take,take线程指向put2,但由于是公平策略,于是队头出队
执行后put1线程被唤醒,take1线程的 take()方法返回了1(put1线程的数据),这样就实现了线程间的一对一通信,这时候内部状态如下:
如果是非公平模型,则如下:
使用的是TransferStack
仍然是两个线程进行了put操作,进行自旋的睡眠
然后进行一个take,执行了take操作,就会发现栈顶为put2,然后出栈配对
后进先出的栈在这里体现的淋漓尽致
SynchronousQueue由于内部的线程匹配机制,在大部分的开发中,不会使用,但是线程池中可能会设计,毕竟没有使用AQS,而是CAS,导致不容易理解