在编写类似算法的状态空间搜索时,我有一个带有节点元素的工作队列。我有多个线程可以访问该队列,该线程弹出一个元素,进行一些转换并检查该元素,并可能向该队列添加更多要访问的节点。
我希望程序在队列为空且所有线程都停止工作时停止(因为它们可以添加更多元素,在这种情况下,我们将需要其他线程来帮助处理这些新节点)。
我应该如何进行检查?我目前正在考虑保留一些AtomicBitSet
,跟踪哪些线程正在工作以及哪些线程不在工作,并在位集为空时停止执行。我将在处理程序的run
方法中设置和取消设置以下内容
while (!bitset.isAllUnset()) {
Node node = queue.poll();
if (node == null) {
bitset.unset(THREAD_INDEX);
} else {
bitset.set(THREAD_INDEX);
// HANDLE THE NODE
}
}
有什么推荐的方法可以解决这个问题吗?
答案 0 :(得分:0)
您可以采用以下方法:
答案 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。