当工作队列为空且所有线程均已停止工作时,如何停止执行?

时间:2019-05-24 16:41:30

标签: java multithreading concurrency

在编写类似算法的状态空间搜索时,我有一个带有节点元素的工作队列。我有多个线程可以访问该队列,该线程弹出一个元素,进行一些转换并检查该元素,并可能向该队列添加更多要访问的节点。

我希望程序在队列为空且所有线程都停止工作时停止(因为它们可以添加更多元素,在这种情况下,我们将需要其他线程来帮助处理这些新节点)。

我应该如何进行检查?我目前正在考虑保留一些AtomicBitSet,跟踪哪些线程正在工作以及哪些线程不在工作,并在位集为空时停止执行。我将在处理程序的run方法中设置和取消设置以下内容

while (!bitset.isAllUnset()) {
    Node node = queue.poll();
    if (node == null) {
        bitset.unset(THREAD_INDEX);
    } else {
        bitset.set(THREAD_INDEX);

        // HANDLE THE NODE
    }
}

有什么推荐的方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:0)

您可以采用以下方法:

  1. 创建一个ThreadPool并将初始任务推送到您的队列。
  2. 保留一个线程(您的主线程)作为ThreadPool上的监视器。
  3. 该线程的工作是启动新线程,只要队列不为空,线程池仍然具有剩余容量并为其分配任务。
  4. 已启动的线程将执行其工作,并将结果写回到队列中。
  5. 然后将其返回到池中,您将必须唤醒监视器。
  6. 只要线程池未达到限制并且任务队列不为空,您的主线程就会尝试启动新线程。

答案 1 :(得分:0)

使用ExecutorService,向其提交读取队列的Runnable,并在队列为空时停止运行。

然后调用执行程序服务的awaitTermination()方法,该方法将阻塞直到所有线程完成。

或使用CompletableFuture

CompleteableFuture.allOf(
    CompleteableFuture.runAsync(
        () -> while(!queue.isEmpty()) handle(queue.poll())
    ));

答案 2 :(得分:-1)

我认为这实际上很复杂。我不知道如何使用基于集合的方法编写正确的版本。例如,以下方法是错误的:

public class ThreadsStopWorkingWrong {
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
ConcurrentHashMap activeThreads = new ConcurrentHashMap();
volatile int prozessedCount = 0;
volatile boolean stop = false;
@Interleave(group = ThreadsStopWorkingWrong.class, threadCount = 1)
public void readFromQueue() {
    int prozessAdditionalElements = 1;
    while (!stop) {
        Object element = queue.poll();

        if (element != null) {
            activeThreads.put(Thread.currentThread(), "");
            if (prozessAdditionalElements > 0) {
                prozessAdditionalElements--;
                queue.offer("2");
            }
            prozessedCount++;
        } else {
            activeThreads.remove(Thread.currentThread());
        }
    }
}
@Interleave(group = ThreadsStopWorkingWrong.class, threadCount = 1)
public void waitTillProzessed() throws InterruptedException {
    while (!queue.isEmpty() && !activeThreads.isEmpty()) {
        Thread.sleep(1);
    }
    assertEquals(2, prozessedCount);
}
@Test
public void test() throws InterruptedException {
    queue.offer("1");
    Thread worker = new Thread(() -> readFromQueue());
    worker.start();
    waitTillProzessed();
    worker.join();

}
}

问题是,当您从队列中轮询消息时,您尚未将线程添加到激活集中,因此!queue.isEmpty()&&!activeThreads.isEmpty()变为true。起作用的是使用消息计数器,如以下示例所示:

public class ThreadsStopWorkingCorrect {
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
AtomicLong messageCount = new AtomicLong();
volatile int prozessedCount = 0;
volatile boolean stop = false;
@Interleave(group = ThreadsStopWorkingCorrect.class, threadCount = 1)
public void readFromQueue() {
    int prozessAdditionalElements = 1;
    while (!stop) {
        Object element = queue.poll();
        if (element != null) {
            if (prozessAdditionalElements > 0) {
                prozessAdditionalElements--;
                queue.offer("2");
                messageCount.incrementAndGet();
            }
            prozessedCount++;
            messageCount.decrementAndGet();
        }
    }
}
@Interleave(group = ThreadsStopWorkingCorrect.class, threadCount = 1)
public void waitTillProzessed() throws InterruptedException {
    while (messageCount.get() > 0) {
        Thread.sleep(1);
    }
    assertEquals(2, prozessedCount);
}
@Test
public void test() throws InterruptedException {
    queue.offer("1");
    messageCount.incrementAndGet();
    Thread worker = new Thread(() -> readFromQueue());
    worker.start();
    waitTillProzessed();
    worker.join();

}
}

我使用vmlens(我编写的用于测试多线程软件的工具)测试了两个示例。因此,交错注释。

在基于集合的版本中,某些线程交织导致prozessedCount == 0。 在基于计数器的版本中,prozessedCount始终为2。