如何使用当前线程处理工作队列?

时间:2011-07-11 19:39:39

标签: java multithreading data-structures queue

我使用哪些数据结构来实现以下逻辑?

  1. read()是一种将某些工作负载排队的异步方法
  2. 一次只能运行一个工作负载。
  3. 排队工作负载的第一个线程成为工作线程。它在返回之前处理队列上的所有工作。调用read()的下一个线程将成为新的工作线程,依此类推......
  4. 如果其他线程在工作线程处理队列时调用read(),它们只是添加到队列的末尾并立即返回。
  5. 我知道如何使用ConcurrentLinkedQueue和AtomicBoolean来实现它,但我觉得有更好的方法。

    CLARIFICATION :工作负载包括调用另一个名为read2()的异步方法。 read2()是异步的,但不是线程安全的。当我说工作线程“处理工作负载”时,它只是触发第一个读操作并立即返回。当read2()完成时,它会调用队列上的下一个操作,依此类推。整个API都是异步的。因此,我想避免使用专用的消费者线程(没有真正的需求,并且它对可扩展性有害)。

3 个答案:

答案 0 :(得分:1)

由于检测线程处理队列项的确切状态或不确定的不确定性,我建议每个线程,无论是用户线程还是某个内核线程触发异步回调,到达此系统的队列都会将其工作负载排入队列然后尝试处理队列中的所有项目。如果某些工作负载请求发出非线程安全调用,请仅使用CS / spinner保护此调用 - 您说这些非线程安全调用无论如何都是简短的,因此CS /自旋锁会花费您很少。

转储AtomicBoolean。虽然发现它确实意味着没有线程正在处理队列中的工作负载,但找到它并不意味着线程正在处理队列中的项目:存在第三个状态 - '已完成队列中的项目但未完全绕过清除布尔值'。

RGDS, 马丁

答案 1 :(得分:0)

我认为你的设计可能会制造软件工程中的一些重要规则,其名称目前无法逃脱。理想情况下,你应该让一个班负责一件事,所以要求班级根据外部状态改变他们的责任会给你带来麻烦。

我建议您考虑Producer/Consumer Patter。您的Producer线程会将工作添加到队列中,而Consumer线程将处理该工作。这可以确保您的生产者线程不必关心他们何时负责从队列中获取工作以及他们负责将工作放在队列上时,他们所关心的只是将工作放在队列上。您的消费者线程将专门从队列中获取工作并执行它,这就是它所能做的一切!

根据您的评论进行更新:生产者不必专注,您所做的只是让您的阅读回调入队工作项(我假设它类似于回调,但纠正我如果我错了)这又是:

private static BlockingQueue q;
// Asynchronous callback
public void read()
{
    q.enqueue(new WorkItem());
}

消费者可能必须是专注的,但从概念上讲,专注于消费者与让多个生产者作为消费者的独家角色之间几乎没有区别:

public class Consumer imlpements Runnable {
    private BlockingQueue _queue;
    public Consumer(BlockingQueue q){
        _queue = q;
    }

    public void run(){
        while(true){
            // block on dequeue until there is work in the queue (i.e. never exits the loop)
            WorkItem w = _queue.dequeue();
            w.DoWork();
        }
    }
}

现在你可以在线程池(ExecutorService)中运行两者 Consumer 和Producer(s),它将完全按照你想要的方式运行。请注意,我没有包含线程安全的所有花俏,例如捕获中断异常并正常终止,但这是您可以(并且应该)添加的内容。

答案 2 :(得分:0)

如何简单地制作read synchronized方法呢?

您只需直接呼叫read,而不是将工作负载(“call read”)放入同步队列中。您的线程可能需要等待一段时间才能获得同步锁定,但正如您所说read执行速度非常快,因此它应该不是问题。

(我不是100%确定我理解你的问题,但我认为这是我理解的:所有read都是在新线程上启动方法read2。但似乎read存在一些线程安全问题,因为您不希望(由于无法解释的原因)许多read并行执行。)

编辑:我意识到我不知道read是单个共享对象的方法,静态方法还是存在于许多对象中。如果是后者,那么仅仅使方法synchronized不会有多大好处;你需要在调用read之前或read之内获得一些全局锁定(可能锁定包含read)。