Java 将第一个元素以原子方式添加到ConcurrentLinkedQueue

Java 将第一个元素以原子方式添加到ConcurrentLinkedQueue,java,multithreading,concurrency,lock-free,Java,Multithreading,Concurrency,Lock Free,我想以原子的方式使用a: 几个并发线程将事件推送到队列中,其他一些线程将处理它们。队列未绑定,我不希望任何线程等待或被锁定。然而,读取部分可能会注意到队列变空了。在无锁实现中,读取线程不能阻塞,而只能结束其任务并继续执行其他任务(即作为ExecutorService)。因此,将第一个新事件推送到空队列中的写入程序必须知道它,并且应该重新启动读取器(即,通过向ExecutorService提交新的Runnable)来处理队列。任何进一步提交第二个或第三个事件的线程都不会在意,因为它们可能会假定某些

我想以原子的方式使用a:

几个并发线程将事件推送到队列中,其他一些线程将处理它们。队列未绑定,我不希望任何线程等待或被锁定。然而,读取部分可能会注意到队列变空了。在无锁实现中,读取线程不能阻塞,而只能结束其任务并继续执行其他任务(即作为ExecutorService)。因此,将第一个新事件推送到空队列中的写入程序必须知道它,并且应该重新启动读取器(即,通过向ExecutorService提交新的Runnable)来处理队列。任何进一步提交第二个或第三个事件的线程都不会在意,因为它们可能会假定某些阅读器已经准备好/提交

不幸的是,ConcurrentLinkedQueue的方法总是返回true。在添加事件之前或之后询问队列是否为isEmpty(),将没有帮助,因为它不是原子的。 我应该使用一些额外的AtomicInteger来监视队列大小()还是有更智能的解决方案


Dieter.

我不太明白为什么您不直接使用
Executor Service
。它在内部使用
阻塞队列
,并负责所有信令本身

// open ended thread pool
ExecutorService threadPool = Executors.newFixedThreadPool(1);
for (Job job : jobsToDo) {
    threadPool.submit(new MyJobProcessor(job));
}
除非你有充分的理由,否则我不会自己重写同样的逻辑


如果您试图以某种方式利用休眠线程,我强烈建议您不要麻烦。线程相对便宜,因此分配一个线程来处理排队的任务是可以的。重复使用线程是不必要的,对我来说似乎是过早的优化。

使用
AtomicInteger
解决提交争用比锁或
同步
块更有效

  • 这是

  • 还有比ConcurrentLinkedQueue更多的


扩展
ConcurrentLinkedQueue
,重写
add()
方法,如果需要,让它启动处理器线程。您可能会遇到两个或多个处理器线程被触发的竞态情况,但它们可以通过同步锁处理这种情况-唯一会发生的阻塞是在即将死亡的线程上,无论如何,不会阻止任何合法的工作线程。如果需要比actor更高的吞吐量或更低的延迟,这取决于需求(轻量级线程,低内存占用)或中断器(操作系统线程,预分配缓冲区)可以使用实现:嗯,当然@Andriy,但除非探查器告诉您,
ExecutorService
是您的瓶颈,否则没有必要让事情变得更复杂。我已经这么做了。我提到了一个线程在使用队列,但它确实是我的底层ExecutorService的一个可运行任务。直到提交的处理器被探查器调用为止准备开始处理事件一些其他事件可能已排队,但我不想提交其他处理器。最后,在处理所有事件后,处理器任务不能简单地等待(与阻塞队列一样),因为它将阻塞ExecutorService的当前线程。相反,它必须终止(可运行的任务,而不是线程),如果出现进一步的事件,则必须重新启动。
同时,我认为一些AtomicInteger将完成这项工作。昨晚,我为MPSCQueue、AoMP和CLQ运行了单线程基准测试(使用谷歌卡尺)。我通常看到CLQ为35ns,其他为500-600ns,CPU峰值很高(可能是由于总线争用)。我的自定义无锁环形缓冲区性能最好,但我还没有弄清楚如何安全地处理CQL溢出。在我的笔记本电脑中,MPSCQueue的性能比CLQ高出约1.5倍:我重写了,因为我在本地丢弃了它。结果类似。我到处玩,还没有弄清楚为什么基准测试结果差异如此之大。您的基准测试无法执行任何JVM预热或尝试评估,直到性能达到稳定状态,因此早期运行有可能受到不公平的惩罚。我的最佳猜测是,Caliper同时运行多个测试,而TAS风格的cas操作正在导致总线风暴和CPU峰值(没有利用mesi缓存一致性进行一级自旋)。当然,这可能只是一个失败的基准测试。如果您可以将我的基准测试移植到您的环境中,那就太好了。我不会这么盲目地相信基准测试框架……以下是证明MPSCQueue性能更好的简单测试和输出: