Java - 具有两个队列的ThreadPool。或者两个队列合二为一

时间:2013-06-28 18:05:40

标签: java multithreading queue threadpool

短版本 - 我正在寻找最有效的方法将两个队列包装成一个用于线程池,其中优先级给予其中一个队列的内容,只要它是非空的然后下降回到第二个队列。

长版: 系统中的几个线程池,每个线程池都有自己的队列。

“外部”请求来自外部并被提交到其中一个池处理一段时间,然后转移到另一个队列。在第一次转移后,请求被视为“内部”,直到完成为止。请求可以在所有线程池之间传输,并且可能最终在它开始的那个中。

我想要做的是,如果每个线程池的队列包含内部请求,则应首先处理它们,直到队列用完内部请求,然后它应切换到外部请求。如果显示新的内部,则应该接下来处理。

不需要纯FIFO,目前每个线程池使用一个LinkedTransferQueue,因为这是我在许多消费者和许多生产者中发现的最快的FIFO队列。非常高的插入率和LinkedTransferQueue中的自旋锁效果非常好。

队列的大小在其他地方处理(因为拒绝),所以我真的只需要正确的投放/接受行为。

过去尝试过无法解决的解决方案:

  1. PriorityBlockingQueue - 太慢了。锁是坏的,特别是在我的硬件上由于某种原因。 (2.4 ghz四核笔记本电脑几乎胜过2x 2.0ghz六核服务器)
  2. 手动包装两个LinkedBlockingQueue - 工作正常,但也太慢了。使用ONE LinkedBlockingQueue也太慢了。
  3. 手动包装两个ArrayBlockingQueue - 与LinkedBlockingQueue相同。
  4. 关于解决方案的想法:

    • 手动“合并”两个LinkedTransferQueues ......但看了之后 它的代码我恐怕没有足够的聪明才能做到这一点 打破了一些东西。
    • 旋转一段时间,在两个队列上做一个偷看(),直到找到一些东西......这基本上是一个自旋锁,我想我可以做一段时间,但我想我想要同样的行为作为LinkedTransferQueue,即。旋转一会儿,然后屈服或睡一会儿。但我没办法醒来。 (编辑:如果我能避免“真正的”锁定,我真的更喜欢)

    不能只在外部做一个peek(),而在内部是if null,因为有些东西可能被插入到External中,我将被阻塞等待内部。我永远不会从内部取消阻止,因为除非我从外部处理它们,否则事物不会在内部结束...如果我将它旋转180度(除非它来自系统内部),同样的方式

    我对一篇毫无意义的帖子道歉......

1 个答案:

答案 0 :(得分:0)

尝试了另一种方法,一个队列提供另一个队列,当有太多“内部”请求被处理时阻塞...锁定性能减半。

最后我最终做到了这一点: 将两个LinkedTransferQueues放在实现BlockingQueue的类中。 让“offer”方法决定最终的队列,take方法执行以下操作(不是确切的代码):

@Override
public Runnable take() throws InterruptedException {
    int c = 0;
    ThreadLocalRandom rand = ThreadLocalRandom.current();

    while (true) {
        // Check internal first
        Runnable r = internalQueue.poll();
        if (r != null) {
            return r;
        }

        // Spin for a while on external, with occasional yield.
        while(c < 128)
        {
            r = externalQueue.poll();
            if (r != null) {
                return r;
            }

            if(rand.nextInt(32) == 0)
            {
                Thread.yield();
            }

            c++;
        }

        // Use the transfer queues time limited poll.
        r = externalQueue.poll(10000, TimeUnit.NANOSECONDS);
        if (r != null) {
            return r;
        }
    }
}

信不信由你,这实际上将吞吐量提高了15%,并将延迟减少了一半。较少的“已启动但未完成”请求占用资源,并且可能在队列中的争用较少(由于我的使用场景)。

自旋锁的值和想法取自LinkedTransferQueue的代码。

缺点是,使用上面的代码,系统空闲率为11%,但通过在30秒不活动后增加轮询超时可以轻松解决这个问题。

如果有人碰巧碰到某一天,仍然欢迎更好的解决方案,但现在这种方法很有效。