
这段时间在网上看了下Disruptor框架内容,写过helloworld级的disruptor框架代码,写这篇博文只是用来总结下Disruptor框架的特点
网上介绍Disruptor的文章很多,总得来说它是个性能较好的高并发框架,LMAX外汇交易平台正是基于Disruptor框架的应用,可见Disruptor在高并况下处理业务的性能还是比较牛逼的,大致总结了下它有以下几个特点。
一提到并发,就会联想到资源共享,有共享就会有竞争,java中常见的同步机制通过代码层“锁”控制资源的共享。锁在解决线程间数据共享安全问题的同事,带来的是性能下降,因为每个线程运行都有自己一块从内存中拷贝过来数据,在自己的“工作区”运行,碰到共享数据,线程之间的写势必会引起上下文的切换,随之而来的就是性能下降。

“无锁读写”。Disruptor采用的是CAS原子操作,对于数据的写通过CAS无锁机制解决了锁带来的问题,这在性能上可是大大提升。CAS操作是基于CPU指令级别的,线程之间的切换不会引起上下文改变,并同时通过内存屏障保证了线程顺序。disruptor总而言之,尽管Disruptor对外宣称是无锁的,但是任何数据共享,势必有竞争,只是原先从代码层深入到了CPU 指令级别,相对而言这个性能提升还是蛮大的。(有关内存屏障和JA内存模型 请参考 )
“数组取代队列”。Disruptor的大框架的模式是经典的生产者-消费者模式,但Disruptor所采用的数据源结构是数组,取代queue或者是LinkedList。因为考虑到性能问题,采用数组有一个优势就是:数组中的数据内存地址是连续的,这样一来有个好处就是CPU预缓存,现代CPU从内存中读取数组中某一个值A的时候,会吧整个数组的数据预先加载到缓存中,下次CPU再去访问数值B的时候,就无须再从内存中加载,直接读取缓存,而读取缓存的速度要远远大于从内存中直接加载。当然你会问,数组也有它的短处,至少扩容这方面会显得比较尴尬,当然还有一个伪共享,但是Disruptor中的核心“任务”RingBuffer就很好的解决了数组扩容问题,先卖个关子,下
面会详细解释
“伪共享的解决”。首先得了解下伪共享是什么?怎么来的?会有什么影响?首先来说下伪共享,只能盗用下别人的图片()
内存的基本单位是字节吧,但是缓存它不是,估计是为了提高flush效率,缓存的基本单位是缓存行,一个缓存行多大呢?一般一个缓存行是64个字节,也就是说能处理8个long,假设一个数组有8个long类型数据,线程1想修改数组中的X,线程2想修改数组中的Y,本来两个不同数据不会有问题,但是问题就在于这个数组同时处在同一个缓存行,结果事情就会像这样发生:当线程1要更新X时,CPU之间通信协议会将线程2的缓存行失效,先由线程1更新完将数据flush到cache3当中,若线程2获得授权,线程1的缓存将会失效,这是一个缓存行共享锁引起的竞争,这样的性能消耗不亚于代码同步机制
上面已经讲述了伪共享的问题以及影响,那么在Disruptor中如何解决?填充空余数据,disruptor团队在缓冲区的64个字节填满,这样CPU从内存加载数据到缓冲区的时候就只能加载一个。
“避免大量GC”。由于java虚拟机对垃圾回收的具体时间不清楚,但类似抢占线程一样呢,会追踪程序运行中有用对象,以及失去引用的对象,一旦发现有对象失去引用,变回在未来某一时刻对其内存地址进行回收。这无疑增加了性能负担,而Disruptor团队在权衡内存和性能时,选择了后者。RingBuffer其实是一个数组环,有个next引用指向后一个event对象,通过set方法将对象进行插入,如果到达末端,便会覆盖之前的数据,以此不断循环处理。disruptor这样的好处能尽最大程度避免GC对event对象的回收,要知道在大吞吐量情况下,GC如果长时间用来出来百万级别的event是相当耗性能,但同时也牺牲了内存。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-26631-1.html
早上好