Java的内置队列
队列 | 有界性 | 锁 | 数据结构 |
---|---|---|---|
ArrayBlockingQueue | bounded | 加锁 | arraylist |
LinkedBlockingQueue | optionally-bounded | 加锁 | linkedlist |
ConcurrentLinkedQueue | unbounded | 无锁(CAS) | linkedlist |
LinkedTransferQueue | unbounded | 无锁(CAS) | linkedlist |
PriorityBlockingQueue | unbounded | 加锁 | heap |
DelayQueue | unbounded | 加锁 | heap |
队列的底层一般分成三种:数组、链表和堆。其中,堆一般情况下是为了实现带有优先级特性的队列
Disruptor核心
Disruptor
Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章种介绍了LMAX是一种 新型零售金融交易平台,它能够以很低的延迟产生大量交易,这个系统是建立在jvm平台上, 其核心是一个业务逻辑处理器:
Disruptor为什么高性能
系统缓存优化-消除伪共享
Disruptor 核心原理
Disruptor 核心-Sequence
Disruptor 核心-Sequencer
Disruptor 核心-Sequencer Barrier
Disruptor 核心-Event
Disruptor 核心-EventProcessor
Disruptor 核心-EventHandler
Disruptor 核心-WorkHandler
Disruptor 核心-WorkProcessor
确保每个Sequence只被一个processor消费,在同一个workPool中处理多个WorkProcessor不会消费同样的Sequence
Disruptor 核心-WaitStrategy
BlockingWaitStrategy
最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现, 内部使用了ReentrantLock
SleepingWaitStrategy
性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小, 适用于异步日志类的场景
YieldingWaitStrategy
性能是最好的,适用于低延时的系统. 在要求极高性能且事件处理线程数小于CPU逻辑核心数的场景中, 推荐使用此策略;例如,CPU开启超线程的特性.
该策略慎用: 内部采用Thread.yield();会让消费线程让出cpu,但又会在让出后同时竞争资源,导致CPU飙升.
disruptor.shutdown();
WorkerPool实现多消费者模式
在单消费者模式中,虽然一个Handler对应一个线程,但是这些Handler处理的是同一个数据,所以是单消费者模式.
多消费者模式: 多个线程处理的是不同的数据,队列中的数据只会被一条线程处理.
Disruptor中消费线程 串行操作(单消费者模式)
// 一个Handler对应一条线程,串行操作
disruptor.handleEventsWith(new Handler1())
.handleEventsWith(new Handler2())
.handleEventsWith(new Handler3());
Disruptor中消费线程 并行操作(单消费者模式)
// 一个Handler对应一条线程,并行操作
disruptor.handleEventsWith(new Handler1());
disruptor.handleEventsWith(new Handler2());
disruptor.handleEventsWith(new Handler3());
// 一个Handler对应一条线程,并行操作
disruptor.handleEventsWith(new Handler1(), new Handler2(), new Handler3());
Disruptor中消费线程 串并行混合操作(单消费者模式)
//Handler1 Handler2 并行执行完成后,串执行Handler3
disruptor.handleEventsWith(new Handler1(), new Handler2())
.handleEventsWith(new Handler3());
//Handler1 Handler2 并行执行完成后,串执行Handler3
EventHandlerGroup group = disruptor.handleEventsWith(new Handler1(), new Handler2());
group.then(new Handler3());
6边形操作(单消费者模式)
--> h1 --> h2 -->
-- --
start --> --> h3
-- --
--> h4 --> h5 -->
h1和h4并行,h1和h2串行,h4和h5串行,h2和h5全部完成后执行 h3
Handler1 h1 = new Handler1();
Handler2 h2 = new Handler2();
Handler3 h3 = new Handler3();
Handler4 h4 = new Handler4();
Handler5 h5 = new Handler5();
disruptor.handleEventsWith(h1,h4);
disruptor.after(h1).handleEventsWith(h2);
disruptor.after(h4).handleEventsWith(h5);
disruptor.after(h2,h5).handleEventsWith(h3);
线程池设置参考规则
AOS架构
AQS是AbstractQueuedSynchronizer的简称。AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架.
isHeldExclusively 方法: 该线程是否正在独占资源
tryAcquire/tryRelease方法: 独占的方式尝试获取和释放资源
tryAcquireShared/tryReleaseShared方法: 共享方式
参考: https://www.jianshu.com/p/da9d051dcc3d
ReentrantLock 重入锁
独享锁:该锁每一次只能被一个线程所持有。
共享锁:该锁可被多个线程共有,典型的就是ReentrantReadWriteLock里的读锁,它的读锁是可以被共享的,但是它的写锁确每次只能被独占。
com.lmax.disruptor.SingleProducerSequencer.next(int) 源码分析
@Override
public long next(int n)
{
if (n < 1)
{
throw new IllegalArgumentException("n must be > 0");
}
long nextValue = this.nextValue;
long nextSequence = nextValue + n;
//wrapPoint用于判断当前的序号有没有绕过整个RingBuffer容器
//相当于标记生产者在队列中的逻辑队尾位置,
long wrapPoint = nextSequence - bufferSize;
//记录最小消费者序号
long cachedGatingSequence = this.cachedValue;
//如果生产者序号大于最小消费者序号,则可能需要等待
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
{
cursor.setVolatile(nextValue); // StoreLoad fence
//最小的序号
long minSequence;
//如果生产者序号大于消费者中最小的序号,则自旋,等待空间
while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue)))
{
LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
}
this.cachedValue = minSequence;
}
this.nextValue = nextSequence;
return nextSequence;
}