再谈ArrayBlockingQueue/LinkedBlockingQueue和Disruptor
1 ArrayBlockingQueue
循环数组
一把锁
3个属性伪共享
2 LinkedBlockingQueue
无法避免在队列中只有一个元素时的线程安全问题。
为解决上述问题,LinkedBlockingQueue的head节点始终指向一个空节点(在构造时,会将head指向一个空节点),则即为设定了当集合中只有一个元素(空节点)时认定集合为空时,不再出队
3 为什么array~不用两把锁
3.1 b.It may be that Doug Lea didn't feel that Java needed to support 2 different BlockingQueues that differed only in their allocation scheme.
https://stackoverflow.com/questions/50739951/what-is-the-reason-to-implement-arrayblockingqueue-with-one-lock-on-tail-and-hea
3.2
3.3
4 disruptor
4.1 数组,CPU缓存友好
4.2 生产消费比链表性能好
4.3 ring buffer大小2的n次方,适配位运算
4.4 cas
4.5 伪共享
5 Disruptor 3.2.1版本源码
5.1 RingBuffer/MultiProducerSequencer
5.1.1
long[] paddedValue = new long [15]
5.1.2 offset放在第8个long,两边追加7个long(56字节)防伪共享
static {
int base = UNSAFE.arrayBaseOffset(long[].class);
int scale = UNSAFE.arrayIndexScale(long[].class);
VALUE_OFFSET = (long)(base + scale * 7);
}
5.1.3 前值
public long get() {
return UNSAFE.getLongVolatile(this.paddedValue, VALUE_OFFSET);
}
5.1.4 cas取得可写入位置,更改成功意味着这个槽当前线程拿下了
public boolean compareAndSet(long expectedValue, long newValue) {
return UNSAFE.compareAndSwapLong(this.paddedValue, VALUE_OFFSET, expectedValue, newValue);
}
5.2 com.lmax.disruptor.WorkProcessor#run
5.2.1 新消费者Sequence
com.lmax.disruptor.WorkerPool#WorkerPool(com.lmax.disruptor.RingBuffer<T>, com.lmax.disruptor.SequenceBarrier, com.lmax.disruptor.ExceptionHandler, com.lmax.disruptor.WorkHandler...)
5.2.2 cas消费
do {
nextSequence = this.workSequence.get() + 1L;
this.sequence.set(nextSequence - 1L);
} while(!this.workSequence.compareAndSet(nextSequence - 1L, nextSequence));
5.3 取模
private int calculateIndex(long sequence) {
return (int)sequence & this.indexMask;
}
///
this.indexMask = bufferSize - 1;
内存屏障
StoreStore:每个Volatile写操作前加上StoreStore屏障,禁止上面的普通写和它重排;
StoreLoad:每个Volatile写操作后面加StoreLoad屏障,禁止跟下面的Volatile读和它重排;
LoadLoad:每个Volatile读操作前面加LoadLoad屏障,禁止跟上面的普通读和Volatile读重排;
LoadStore:每个Volatile读操作后面加LoadStore屏障,禁止禁止下面的普通写和Volatile读重排;
、