并发解谜:Java Concurrency - Cyclicbarrier。正确用法?
Posted
技术标签:
【中文标题】并发解谜:Java Concurrency - Cyclicbarrier。正确用法?【英文标题】:Concurrent puzzle solving: Java Concurrency - Cyclicbarrier. Correct usage? 【发布时间】:2014-05-19 20:11:46 【问题描述】:我正在尝试编写一个程序来解决 2 个无法相互独立解决但具有相同解决方案的谜题。我的想法是它们都在一个单独的线程中运行,直到它们停止寻找新的信息。然后他们通过更新一些共享状态变量来传达他们发现的内容,并在其中任何一个写入共享状态时继续。
我认为 CyclicBarrier 是在这里使用的合适机制。这是我的代码(在 2 个线程中同时运行:
while (true)
doSolvingLogicHere();
shareUpdates(); // this method updates the shared state variable and is synhronized
int count;
int updates = 0;
try
count = writeBarrier.await();
updates = threadsUpdatedSomething;
if (count == 0)
writeBarrier.reset();
threadsUpdatedSomething = 0; //'reset' the shared value
catch (InterruptedException ex)
Logger.getLogger(TwinSolver.class.getName()).log(Level.SEVERE, null, ex);
catch (BrokenBarrierException ex)
Logger.getLogger(TwinSolver.class.getName()).log(Level.SEVERE, null, ex);
if (updates == 0) //no thread updated something
break;
else // at least one of the threads updated something, solving should continue in both threads
readUpdates();
ThreadsUpdatedSomething 是一个共享整数,如果线程更新了任何内容,则在“ShareUpdates()”中递增。当两个线程在迭代中都没有找到任何新的东西时,这意味着它们永远不会找到任何新的东西,并且应该为两个线程停止整个循环。这就是为什么我要检查它是否为零。
当两个线程都没有在共享状态变量中写入任何新信息时,我希望它们都停止。但是在运行程序时,其中一个线程停止,而另一个继续运行。调试程序并在“readUpdates()”行设置断点时,程序按预期运行。
这是处理这种并发“解决”循环的正确方法吗?如果它是正确的,我的代码中的错误在哪里?
感谢您的帮助!
编辑:纠正了小错误。 '更新 = 线程更新的东西;'现在在正确的地方
【问题讨论】:
【参考方案1】:按照API,await
返回
the arrival index of the current thread, where index getParties() - 1 indicates the first to arrive and zero indicates the last to arrive
count = writeBarrier.await();
话虽如此,所以只有一个 Thread 会收到 0 。并且只有一个线程会将updates
的值设置为 0。这就是为什么最后到达的线程停止而另一个没有停止的原因。
根据您的陈述,当您发现两个线程都没有更新threadsUpdatedSomething时,您需要停止线程。我假设时间 threadsUpdatedSomething 将为零。 如果不是你必须改变逻辑,一些如何找到条件何时必须被打破并应用它
while (true)
doSolvingLogicHere();
shareUpdates(); // this method updates the shared state variable and is synhronized
int count;
int updates = 0;
try
writeBarrier.await();
if (threadsUpdatedSomething == 0)
updates = threadsUpdatedSomething;
writeBarrier.reset();
threadsUpdatedSomething -= 2; //'reset' the counter by decrementing 2
catch (InterruptedException ex)
Logger.getLogger(TwinSolver.class.getName()).log(Level.SEVERE, null, ex);
catch (BrokenBarrierException ex)
Logger.getLogger(TwinSolver.class.getName()).log(Level.SEVERE, null, ex);
if (updates == 0) //no thread updated something
break;
else // at least one of the threads updated something, solving should continue in both threads
readUpdates();
如果需要,也不要忘记在异常情况下设置中断条件。
【讨论】:
感谢您的意见!我在“更新”分配和“-=2”操作的位置上犯了一个小错误。我想做的是只有最后一个线程重置屏障,所以使用 await() 的返回值应该是正确的。而不是检查更新== 0,我可以只检查threadsUpdatedSomething var,但由于可能的竞争条件,这个值可能会过时,所以这实际上是我使用'updates'变量的原因。我觉得我让它太复杂了,但我真的看不出协调线程的正确算法。 我没有看到比赛场景。由于您的共享变量更新发生在 await() 语句之前。在等待语句之后,除了您要退出的场景(如果块)之外没有更新。以上是关于并发解谜:Java Concurrency - Cyclicbarrier。正确用法?的主要内容,如果未能解决你的问题,请参考以下文章
java并发编程实战(java concurrency in practice)
Java 7 Concurrency Cookbook 翻译 序言
深入了解Java并发——《Java Concurrency in Practice》13.显式锁
深入了解Java并发——《Java Concurrency in Practice》14.构建自定义的同步工具