Java 为什么“ExecutorService”不能一致地调度线程?
我正在尝试使用Java 为什么“ExecutorService”不能一致地调度线程?,java,multithreading,synchronization,cyclicbarrier,Java,Multithreading,Synchronization,Cyclicbarrier,我正在尝试使用CyclicBarrier重新实现我的并发代码,这对我来说是新的。我可以不使用它,但在我的其他解决方案中尝试它时,我遇到的问题是以下代码出现死锁情况: //instance variables (fully initialised elsewhere). private final ExecutorService exec = Executors.newFixedThreadPool(4); private ArrayList<IListener> listeners
CyclicBarrier
重新实现我的并发代码,这对我来说是新的。我可以不使用它,但在我的其他解决方案中尝试它时,我遇到的问题是以下代码出现死锁情况:
//instance variables (fully initialised elsewhere).
private final ExecutorService exec = Executors.newFixedThreadPool(4);
private ArrayList<IListener> listeners = new ArrayList<IListener>();
private int[] playerIds;
private class WorldUpdater {
final CyclicBarrier barrier1;
final CyclicBarrier barrier2;
volatile boolean anyChange;
List<Callable<Void>> calls = new ArrayList<Callable<Void>>();
class SyncedCallable implements Callable<Void> {
final IListener listener;
private SyncedCallable(IListener listener) {
this.listener = listener;
}
@Override
public Void call() throws Exception {
listener.startUpdate();
if (barrier1.await() == 0) {
anyChange = processCommons();
}
barrier2.await();
listener.endUpdate(anyChange);
return null;
}
}
public WorldUpdater(ArrayList<IListener> listeners, int[] playerIds) {
barrier2 = new CyclicBarrier(listeners.size());
barrier1 = new CyclicBarrier(listeners.size());
for (int i : playerIds)
calls.add(new SyncedCallable(listeners.get(i)));
}
void start(){
try {
exec.invokeAll(calls);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void someMethodCalledEveryFrame() {
//Calls some Fisher-something method that shuffles int[]
shufflePIDs();
WorldUpdater updater = new WorldUpdater(listeners, playerIds);
updater.start();
}
//实例变量(在别处完全初始化)。
private final ExecutorService exec=Executors.newFixedThreadPool(4);
私有ArrayList侦听器=新ArrayList();
私有int[]playerIds;
私有类世界更新程序{
最后一个自行车道挡泥板1;
最后一个自行车道挡泥板2;
不稳定的布尔变化;
列表调用=新建ArrayList();
类SyncedCallable实现可调用{
最终的听者;
专用SyncedCallable(IListener侦听器){
this.listener=listener;
}
@凌驾
public Void call()引发异常{
listener.startUpdate();
if(barrier1.await()==0){
anyChange=processCommons();
}
障碍2.等待;
endUpdate(anyChange);
返回null;
}
}
公共世界更新程序(ArrayList侦听器,int[]playerId){
barrier2=新的CyclicBarrier(listeners.size());
barrier1=newcyclicbarrier(listeners.size());
for(int i:玩家ID)
calls.add(newsyncedcallable(listeners.get(i));
}
void start(){
试一试{
exec.invokeAll(调用);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
}
void someMethodCalledEveryFrame(){
//调用一些Fisher-something方法来洗牌int[]
shufflepis();
WorldUpdater updater=新的WorldUpdater(侦听器、播放器ID);
updater.start();
}
在此阶段,我使用Android Studio(intelliJ)中的调试器暂停执行。我得到多个线程,显示mywait
调用作为最后一个要执行的代码
->不安全。停车
->LockSupport.park
->AbstractQueuedSynchronizer$ConditionObject.Wait
->CyclicBarrier.doWait
->CyclicBarrier.等待
至少有一个线程将具有此堆栈:
->不安全。停车
->LockSupport.park
->AbstractQueuedSynchronizer$ConditionObject.Wait
->LinkedBlockingQueue.take
->ThreadPoolExecutor.getTask
->ThreadPoolExecutor.runWorker
->ThreadPoolExecutor$Worker.run
->Thread.run
我注意到,CyclicBarrier
在这些散乱的线程中不起作用
processCommons
正在调用exec.invokeAll
(在3个侦听器上),我想这意味着线程用完了。但是很多时候这种情况并没有发生,所以请有人澄清为什么ExecutorService
不能一致地调度我的线程?他们有自己的堆栈和程序计数器,所以我认为这不是问题。我一次最多只能跑4次。有人帮我学数学吗?我做了一个小实验,得出了:
@Override
public Void call() throws Exception {
System.out.println("startUpdate, Thread:" + Thread.currentThread());
listener.startUpdate();
if (barrier1.await() == 0) {
System.out.println("processCommons, Thread:" + Thread.currentThread());
anyChange = processCommons();
}
barrier2.await();
System.out.println("endUpdate, Thread:" + Thread.currentThread());
listener.endUpdate(anyChange);
return null;
}
当使用3个侦听器和3个侦听器的池时,我将始终挂起processCommons
,其中包含以下内容:
List<Callable<Void>> calls = new ArrayList<Callable<Void>>();
for (IListener listiner : listeners)
calls.add(new CommonsCallable(listener));
try {
exec.invokeAll(calls);
} catch (InterruptedException e) {
e.printStackTrace();
}
应该更好,但事实并非如此
最后,我在intelliJ中执行了断点步进(感谢ideaC!)
问题是
if (barrier1.await() == 0) {
anyChange = processCommons();
}
barrier2.await();
在2个await
之间,可能会有几个挂起的线程实际上还没有到达await
。在4个侦听器中有3个侦听器的情况下,只需一个侦听器就可以获得“未计划”(或任何内容),并且barrier2
永远不会获得完整的补码。但是当我有8人的时候呢?除了两个线程之外,其他线程都表现出相同的行为:
->帕克先生
->洛克公园酒店
->AbstractQueuedSynchronizer$ConditionObject.await
->LinkedBlockingQueue.take
->ThreadPoolExecutor.getTask
->ThreadPoolExecutor.runWorker
->ThreadPoolExecutor$Worker.run
->Thread.run
这里会发生什么情况来禁用所有5个线程?我应该采纳詹姆斯·拉格的建议,避免在这个过于复杂的CyclicBarrier
——更新——它现在可以在没有CyclicBarrier
的情况下通宵运行listeners.size()
在创建WorldUpdater
时的价值是什么?如果超过四个,那么线程将永远无法通过屏障
您的ExecutorService
正好有四个线程。不多不少。barrier1.await()
和barrier2.await()
的调用者只有在侦听器.size()
线程完全等待时才会越过该障碍
我的直觉反应是,池线程使用
CyclicBarrier
将是一个错误CyclicBarrier
只有在您确切知道有多少线程将使用它时才有用。但是,当您使用线程池时,通常不知道池的大小。事实上,在现实世界(即商业)应用程序中,如果您使用的是线程池,那么它可能根本不是由您的代码创建的。它可能是在其他地方创建的,并作为注入依赖项传递到您的代码中。我最初批准这一点是因为您强调“正好四个”,“不多,不少”,这似乎意义深远。我现在意识到这是错误的:就像我尝试使用CyclicBarrier
。它只是不适合这里,所以谢谢你的指针。
if (barrier1.await() == 0) {
anyChange = processCommons();
}
barrier2.await();