前言
Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。
其实Disruptor与其说是一个框架,不如说是一种设计思路,这个设计思路对于存在“并发、缓冲区、生产者—消费者模型、事务处理”这些元素的程序来说,Disruptor提出了一种大幅提升性能(TPS)的方案。
- 来一张全链路流程图
- disruptor设计理念很超前,解决了传统队列的痛点
1、false-sharding:CPU伪共享问题2、无锁编程的极致体验-CAS3、两个独立的线程之间高效交换数据
一、锁的代价
Disruptor论文中讲述一个实验,一个计数器循环自增5亿次
- 场景1:单线程无锁时,程序耗时300ms
- 场景2:单线程有锁,程序需要耗时10000ms
- 场景3:双线程有锁,耗时224000ms
- .........
Why?
简而言之就是多线程锁竞争导致的上下文切换时间成本远远大于了线程持有锁的性能损耗
- ArrayBlockQueue伪代码分析
public void put(E e) {
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
//当队列满时,调用notFull.await()方法,阻塞写线程。
while (count == items.length) {
notFull.await(); //Condition条件阻塞
}
//把元素 e 插入到队尾
insert(e);
//解锁
lock.unlock();
//若队列为空,激活读线程
notEmpty.signal();
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
//当队列空时,阻塞读线程
while (count == 0) {
notEmpty.await();
}
//取出队头元素
E x = extract();
//若队列full,激活写线程
notFull.signal();
return x;
//解锁
finally{
lock.unlock();
}
}
- tail(takeIndex)和head(putIndex)指针都是锁竞争的冲突点
队列的目的就是为生产者和消费者提供一个地方存放要交互的数据,缓冲上下游的消息,实际场景中缓冲常常是满的(生产者比消费者快)或者空的(消费者比生产者快)。生产者和消费者能够步调一致的情况非常少见。 - ArrayBlockQueue是悲观锁的一种体现,读写线程都假设存在冲突, 多线程并发场景下,性能很差
- 接下来让我们看看Disruptor的实现
disruptor根本就不用锁,取而代之-CAS&