这就是伪共享,因为每次你访问A的同时,你也会得到B,而且每次你访问B,同时你也会得到A。他们仿佛是一体的,但是实际没有任何关系。
引入缓存行填充机制,在RingBuffer中,需要有一个指向当前数据区的序列号(Sequencer),在有生成者和消费者对RingBuffer进行,数据读写的时候,我们对这个序列号进行缓存行填充机制,保证一个序列号在内存中,占有一个缓冲行。
通过代码演示,我们也可以看到的确存在差异,而且随着读写的线程变多,这样的差距越大。
false share code
官方给到的代码,没有进行完全填充(也就是没有沾满一个缓存行),我自己写的例子有进行改进。
在典型的消费者/生成者模型中,会存在等待现象,disruptor提供了以下的几种等待策略:
BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现
SleepingWaitStrategy 的性能表现跟 BlockingWaitStrategy 差不多,对 CPU 的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景;
YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于 CPU 逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。
经测试前两种效果差不多,延迟在微秒以内,可以忽略,cpu占用不高,YieldingWaitStrategy模式队列空闲时CPU达到100%,不适合
简单例子
直接使用ringbuffer
public static void main(String[] args) {
int size = 1<<10;
ExecutorService executors = Executors.newCachedThreadPool();
//创建一个disruptor,指定ringbuffer的size和处理数据的factory
Disruptor<TestObject> disruptor = new Disruptor<TestObject>(new TestObjectFactory(), size, executors);
//disruptor里面设置一个处理方式
disruptor.handleEventsWith(new TestObjectHandler());
RingBuffer<TestObject> ringBuffer = disruptor.start();
for (long i = 0; i < 1000; i++) {
//下一个可以用的序列号
long seq = ringBuffer.next();
try {
//这个序列号的slot 放入数据
TestObject valueEvent = ringBuffer.get(seq);
valueEvent.setValue(i);
} finally {
//发布通知,并且这一步一定要放在finally中,因为调用了ringBuffer.next(),就一定要发布,否则会导致disruptor状态的错乱
ringBuffer.publish(seq);
}
}
disruptor.shutdown();
executors.shutdown();
}
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-34283-2.html
表示农民老了只能喝水了
雷达探测精度和灵敏度