Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么“ExecutorService”不能一致地调度线程?_Java_Multithreading_Synchronization_Cyclicbarrier - Fatal编程技术网

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)中的调试器暂停执行。我得到多个线程,显示my
wait
调用作为最后一个要执行的代码

->
不安全。停车

->
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();