这是一个非常具体的问题,但是我们已经停留了一段时间,我在网站上搜索了一个答案而且无法找到它(第一次!)。所以我们走了:
我的数据结构是concurrentHashMap。映射中的每个值都是一个ConcurrentLinkedDeque of Actions(这是我写的一个抽象类,由几个动作扩展 - 与问题无关)。队列被昵称为'演员'。
基本程序流程为:
有一系列线程。每个线程遍历哈希映射,查找要执行的操作。如果操作队列为空,则线程将移动到下一个队列。如果它找到一个动作,该线程将从队列中弹出它并执行该动作。只有在完成所有先前的操作后,才会在程序的运行时间内批量提交新操作。
如果所有队列都为空,会发生什么? 我们希望避免忙碌等待,所以如果所有队列都为空,则线程将进入休眠状态。什么时候醒来?我们保留了一个版本监视器,它基本上计算了向该程序添加了多少动作。线程正在这个柜台上睡觉。
提交给计划的新动作 - >版本计数器递增1 - >线程醒来。
我们如何知道所有队列是否都是空的?嗯,这是一个很好的旧方式 - 我们遍历它们并检查“空”'每一次,同时保持一个'AllAllEmpty'布尔值。
所以这是我们有问题的场景:
- 所有队列都是空的。 - 一个线程遍历所有队列,看看它们是否为空。 - 线程已经检查了某个队列后,会向该队列提交一个新操作。
所以发生了什么:线程认为所有队列都是空的,所以他去睡觉了。他错过了“叫醒”的电话。 (版本号增加了一个),因为当时他没有睡觉(但是在队列中迭代)。没有进一步的行动将提交给该计划,因为旧的一批行动尚未完成。线程等待新操作,操作等待线程。死锁。
我们如何避免这种情况?我们认为使用ConcurrentHashMap应该解决这个问题,但显然迭代它们是不安全的。我们根本不允许使用synchronized。我们只是不知道如何解决它。
我意识到随着线程数量的增加,这个问题变得不太可能,但我们需要能够运行少量线程 - 甚至一个。有一个线程,这种情况经常发生。你会推荐什么?
以下是一些相关代码:
while (!Thread.currentThread().isInterrupted())
{
if(actors.isEmpty())
//if there's no actors, we need to wait until new actor will added to the hash map
{
try {
vm.await(vm.getVersion());//waits until the version monitor will be changed
}
catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
}
//check if all queues are empty
boolean isAllEmpty = true;
Iterator <ConcurrentHashMap.Entry<String, QueuePair>> it2 = actors.entrySet().iterator();
while (it2.hasNext() && isAllEmpty){
if (!it2.next().getValue().queue.isEmpty())
isAllEmpty=false;
}
if(isAllEmpty)//all actors are empty
{
try {
vm.await(vm.getVersion());//waits until the version monitor will be changed
}
catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
}
Iterator <ConcurrentHashMap.Entry<String, QueuePair>>it = actors.entrySet().iterator();
while (it.hasNext())//if there is an available actors queue, execute one of it actions
{
//irrelevant code that executes the action
}
}
}
});
感谢您的回答:)