实现简单工作队列的问题

时间:2010-05-08 20:21:07

标签: java multithreading

我在实施简单的工作队列时遇到了麻烦。做一些分析,我面临一个微妙的问题。工作队列由常规链接列表支持。代码看起来像这样(简化):

0. while (true)  
    1. while (enabled == true)  
        2. acquire lock on the list and get the next action to be executed (blocking operation)  (store it in a local variable)  
        3. execute the action (outside the lock on the list on previous line)
    4. get lock on this work queue  
        5. wait until this work queue has been notified (triggered when setEnabled(true) has been callled)

setEnabled(e)操作如下所示(简化):

enabled = e
if (enabled == true)
    acquire lock on this work queue and do notify()

虽然这有效,但存在发生死锁的情况。它发生在以下罕见情况:

  • 在步骤(3)中执行操作时,调用setEnabled(false)
  • 在进入步骤(4)之前,调用setEnabled(true)
  • 现在步骤(5)一直等待,因为这个工作队列已经被通知但我们错过了

我该如何解决这个问题?我已经看了一段时间,但我无法想出解决方案。

请注意我对线程同步很新。

非常感谢。

5 个答案:

答案 0 :(得分:1)

不确定移动Java上的多线程内存模型。对于Desktop Java,我很确定它在Java 1.5中有一些严重的错误。

使用真正的Java代码通过伪代码更容易进行故障排除..但如果这是一个Java 1.1错误而不是代码错误,我不会感到惊讶......

答案 1 :(得分:0)

如果您认为问题是错过了通知,那么只需存储另一个布尔标志,例如“wasNotified”。然后在步骤5,对wasNotified进行同步检查。如果您收到通知,请重复步骤1,否则等待另一个通知。

您可能还会考虑使用condition variable来实现此功能(尽管如果限制为1.1,则可能无法使用Condition类。)

答案 2 :(得分:0)

我相信以下2个更改将修复死锁错误:

1)按如下方式更改setEnabled:

setEnabled(e){
    acquire lock on Q{
        enabled = e 
        if (enabled == true) 
             do Q.notify() 
    }
}

2)将条件添加到步骤5:

如果enabled = false,则等待通知此工作队列(Q.wait())

此方法保证仅当启用标志当前已关闭时才会发生步骤5的等待。

答案 3 :(得分:0)

你应该确保在循环中检查并等待条件,否则你可能会提前取消等待,这可能导致以后出现死锁:

改变这个:

if ( ! condition )
{
    wait( );
}

到此:

while ( ! condition )
{
    wait( );
}

这直接来自wait的文档。

答案 4 :(得分:0)

您正在尝试创建一个可以启用/禁用的工作程序,该工作程序与在可用时处理任务的工作程序不同。您需要围绕启用状态而不是队列进行同步。

while (true)  
    synchronize (enabled)
        while (!enabled)
            enabled.wait();
    task = taskList.take();
    task.run();

您可以通过在同步块之前首先执行if(!enabled)检查来减少已启用的同步(如果您愿意接受可见性问题)。基本上,破坏的范式称为双重检查锁定。