从许多队列消费

时间:2013-01-21 08:29:38

标签: java concurrency

我有大量的状态机。有时,状态机需要从一个状态移动到另一个状态,这可能是便宜或昂贵的,可能涉及数据库读写等。

这些状态更改是由于来自客户端的传入命令而发生的,并且可以随时发生。

我希望将工作负载并行化。我想要一个队列说'将这台机器从这个状态移动到这个状态'。显然,任何一台机器的命令都需要按顺序执行,但如果我有很多线程,我可以并行移动许多机器。

我可以为每台状态机创建一个线程,但状态机的数量依赖于数据,可能有数百或数千;我不希望每个状态机都有一个专用线程,我想要一个某种类型的池。

如何拥有一个工作池,但要确保每个状态机的命令都是按顺序处理的?

更新 :所以假设Machine实例有一个未完成命令的列表。当线程池中的执行程序完成使用命令时,如果它具有更多未完成的命令,它会将Machine放回到线程池的任务队列中。所以问题是,当你附加第一个命令时,如何以原子方式将Machine放入线程池中?并确保这一切都是线程安全的吗?

3 个答案:

答案 0 :(得分:2)

我建议你这个场景:

  1. 使用Executors.newFixedThreadPool
  2. 创建线程池,可能是一些修复大小
  3. 创建一些结构(可能是HashMap),为每个状态机保留一个Semaphore。信号量的值为1,并且公平信号量保持序列
  4. 在Runnable中,它将执行求解工作,只需为其状态机的信号量添加semaphore.aquire(),并在run方法结束时添加semaphore.release()
  5. 使用线程池的大小,您将控制并行度。

答案 1 :(得分:2)

我建议采用另一种方法。不使用线程池来移动状态机中的状态,而是使用线程池来处理所有事情,包括执行工作。在做了一些导致状态改变的工作之后,应该将状态改变事件添加到队列中。处理完状态更改后,应将另一个do-work事件添加到队列中。

假设状态转换是由工作驱动的,反之亦然,则无法进行顺序处理。

将信号量存储在特殊地图中的想法非常危险。地图必须同步(添加/删除objs是线程不安全的)并且进行搜索(可能在地图上同步)然后使用信号量的开销相对较大。

此外 - 如果你想在你的应用程序中使用多线程架构,我认为你应该一路走下去。混合使用不同的架构可能会在以后引起麻烦。

答案 2 :(得分:1)

每台机器都有一个线程ID。产生所需数量的线程。让所有线程贪婪地处理来自全局队列的消息。每个线程锁定当前消息的服务器以供其自己独占使用(直到它完成处理当前消息及其队列中的所有消息),其他线程将该服务器的消息放入其内部队列。

编辑:处理消息伪代码:

void handle(message)
  targetMachine = message.targetMachine
  if (targetMachine.thread != null)
    targetMachine.thread.addToQueue(message);
  else
    targetMachine.thread = this;
    process(message);
    processAllQueueMessages();
    targetMachine.thread = null;

处理消息Java代码:(我可能会略微过度复杂,但这应该是线程安全的)

/* class ThreadClass */
void handle(Message message)
{
  // get targetMachine from message
  targetMachine.mutexInc.aquire(); // blocking
  targetMachine.messages++;
  boolean acquired = targetMachine.mutex.aquire(); // non-blocking
  if (acquired)
    targetMachine.threadID = this.ID;
  targetMachine.mutexInc.release();
  if (!acquired)
    // can put this before release, it may speed things up
    threads[targetMachine.threadID].addToQueue(message);
  else
  {
    process(message);
    targetMachine.messages--;
    while (true)
    {
      while (!queue.empty())
      {
        process(queue.pop());
        targetMachine.messages--;
      }
      targetMachine.mutexInc.acquire(); // blocking
      if (targetMachine.messages > 0)
      {
        targetMachine.mutexInc.release();
        Thread.sleep(1);
      }
      else
        break;
    }
    targetMachine.mutex.release();
    targetMachine.mutexInc.release();
  }
}