Java等待通知死锁问题

Java等待通知死锁问题,java,multithreading,deadlock,wait,notify,Java,Multithreading,Deadlock,Wait,Notify,这是我的类,它连续执行依赖的Runnables。它所做的是并行执行所有的Runnable,但在完成时,等待队列中的头Runnable首先完成。头部完成后,第二项完成,依此类推 这段代码的问题是它会导致某种死锁。当执行多个任务时,它会停止执行。暂停调试器时,它显示所有线程都在等待wait()语句 /** * Executes all tasks in parallel, with completion handler called only when other tasks of same ke

这是我的类,它连续执行依赖的
Runnable
s。它所做的是并行执行所有的
Runnable
,但在完成时,等待队列中的
Runnable
首先完成。头部完成后,第二项完成,依此类推

这段代码的问题是它会导致某种死锁。当执行多个任务时,它会停止执行。暂停调试器时,它显示所有线程都在等待
wait()
语句

/**
 * Executes all tasks in parallel, with completion handler called only when other tasks of same key are complete.
 * For a given key, the order in which {@link #execute(Object, int, java.util.concurrent.Callable, Runnable, Runnable)} was called will be the order in which completion runnable will be called.
 */
public class DependentExecutor {

    private final Executor executor;
    private final Map<Object, Queue<DependentTask>> allTasks = new ArrayMap<>();
    private final boolean enableDependency;

    public DependentExecutor(boolean enableDependency, Executor executor) {
        this.executor = executor;
        this.enableDependency = enableDependency;
    }

    /**
     * You should return true from the task on successful completion.
     * If task returns false, then completion runnable wont be executed.
     * <p/>
     * This method will return false if tha task with this uniqueId already exists. Otherwise true is returned.
     *
     * @param key                A non null key using which task dependency is decided. Tasks with same key are dependent.
     * @param uniqueId           If there is a task with this uniqueId already present, this task will be rejected
     * @param task               Optional. A long pending task to be performed or null if only completion is to be dependant.
     * @param completionCallback A non null callback which will be serially executed for tasks with same key
     * @param errorCallback      If task returns false, then this callback will be invoked immediately (no dependency)
     */
    public boolean execute(Object key, int uniqueId, Callable<Boolean> task, Runnable completionCallback, Runnable errorCallback) {

        DependentTask queuedTask;
        synchronized (allTasks) {
            Queue<DependentTask> queue = allTasks.get(key);
            for (Map.Entry<Object, Queue<DependentTask>> objectQueueEntry : allTasks.entrySet()) {
                synchronized (objectQueueEntry.getValue()) {
                    Iterator<DependentTask> iterator = objectQueueEntry.getValue().iterator();
                    while (iterator.hasNext()) {
                        DependentTask dependentTask = iterator.next();
                        if (dependentTask.getUniqueId() == uniqueId) {
                            // no 2 tasks can have same uniqueID
                            return false;
                        }
                    }
                }
            }

            if (queue == null && task == null) {
                // this means we have no pending dependency as well as no task to perform. So only callback.
                completionCallback.run();
                return true;
            } else if (queue == null) {
                queue = new LinkedList<DependentTask>();
                allTasks.put(key, queue);
            }
            if (!enableDependency) {
                key = Math.random();
            }
            queuedTask = new DependentTask(key, uniqueId, queue, task, completionCallback, errorCallback);
            queue.add(queuedTask);
        }
        executor.execute(queuedTask);
        return true;
    }

    class DependentTask implements Runnable {

        private final Queue<DependentTask> dependencyQueue;
        private final Callable<Boolean> task;
        private final Object key;
        private final Runnable completionCallback;
        private final Runnable errorCallback;
        private final int uniqueId;

        public DependentTask(Object key, int uniqueId, Queue<DependentTask> dependencyQueue, Callable<Boolean> task, Runnable completionCallback, Runnable errorCallback) {
            this.uniqueId = uniqueId;
            this.task = task;
            this.dependencyQueue = dependencyQueue;
            this.key = key;
            this.completionCallback = completionCallback;
            this.errorCallback = errorCallback;
        }

        public int getUniqueId() {
            return uniqueId;
        }

        @Override
        public void run() {
            Boolean result = false;
            try {
                if (task != null) {
                    result = task.call();
                } else {
                    result = true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (result) {
                    synchronized (dependencyQueue) {

                        while (dependencyQueue.peek() != this) {
                            try {
                                dependencyQueue.wait(); // deadlock !!
                            } catch (InterruptedException e) {
                            }
                        }
                    }
                    completionCallback.run(); // by now we are the first element in the linked list. Lets call completion.
                } else {
                    errorCallback.run(); // by now we are the first element in the linked list. Lets call error callback.
                }
                synchronized (dependencyQueue) {
                    dependencyQueue.remove(); //remove thyself
                    dependencyQueue.notifyAll();
                }

                // clean up of main map
                synchronized (allTasks) {
                    if (dependencyQueue.isEmpty()) {
                        allTasks.remove(key);
                    }
                }
            }
        }
    }
}
/**
*并行执行所有任务,仅当具有相同键的其他任务完成时才调用完成处理程序。
*对于给定的键,调用{@link#execute(Object,int,java.util.concurrent.Callable,Runnable,Runnable)}的顺序将是调用completion Runnable的顺序。
*/
公共类从属执行器{
私人最终执行人;
private final Map allTasks=new ArrayMap();
私有最终布尔启用依赖;
公共DependentExecutor(布尔启用Dependency,Executor-Executor){
this.executor=执行人;
this.enableDependency=enableDependency;
}
/**
*成功完成任务后,应返回true。
*若任务返回false,则不会执行completion runnable。
*

*如果具有此唯一ID的任务已存在,则此方法将返回false。否则返回true。 * *@param key决定任务依赖关系的非空键。具有相同键的任务是依赖的。 *@param uniqueId如果已经存在具有此uniqueId的任务,则此任务将被拒绝 *@param task可选。要执行的长挂起任务,如果仅依赖于完成,则为null。 *@param completionCallback一个非空回调,将对具有相同密钥的任务串行执行 *@param errorCallback如果任务返回false,则将立即调用此回调(无依赖项) */ 公共布尔执行(对象键、int uniqueId、可调用任务、可运行的completionCallback、可运行的errorCallback){ 依赖任务队列任务; 已同步(所有任务){ Queue Queue=allTasks.get(键); 对于(Map.Entry objectQueueEntry:allTasks.entrySet()){ 已同步(objectQueueEntry.getValue()){ 迭代器迭代器=objectQueueEntry.getValue().Iterator(); while(iterator.hasNext()){ DependentTask DependentTask=iterator.next(); if(dependentTask.getUniqueId()==uniqueId){ //没有两个任务可以具有相同的唯一ID 返回false; } } } } if(队列==null&&task==null){ //这意味着我们没有挂起的依赖项,也没有要执行的任务,所以只有回调。 completionCallback.run(); 返回true; }else if(队列==null){ 队列=新的LinkedList(); 所有任务。放置(键、队列); } 如果(!enableDependency){ key=Math.random(); } queuedTask=新的DependentTask(键、唯一ID、队列、任务、completionCallback、errorCallback); 添加(queuedTask); } executor.execute(queuedTask); 返回true; } 类DependentTask实现可运行{ 私有最终队列依赖队列; 私有最终可调用任务; 私有最终对象密钥; 私有最终可运行的completionCallback; 私有最终可运行错误回调; 私人最终国际统一号; public DependentTask(对象键、int uniqueId、队列dependencyQueue、可调用任务、可运行的completionCallback、可运行的errorCallback){ this.uniqueId=uniqueId; this.task=任务; this.dependencyQueue=dependencyQueue; this.key=key; this.completionCallback=completionCallback; this.errorCallback=errorCallback; } public int getUniqueId(){ 返回唯一标识; } @凌驾 公开募捐{ 布尔结果=假; 试一试{ 如果(任务!=null){ 结果=task.call(); }否则{ 结果=真; } }捕获(例外e){ e、 printStackTrace(); }最后{ 如果(结果){ 已同步(依赖队列){ while(dependencyQueue.peek()!=此){ 试一试{ dependencyQueue.wait();//死锁!! }捕捉(中断异常e){ } } } completionCallback.run();//现在我们是链表中的第一个元素。让我们调用completion。 }否则{ errorCallback.run();//现在我们是链表中的第一个元素。让我们调用error callback。 } 已同步(依赖队列){ dependencyQueue.remove();//删除您自己 dependencyQueue.notifyAll(); } //清理主地图 已同步(所有任务){ if(dependencyQueue.isEmpty()){ 所有任务。删除(键); } } } } } }

问题#1

从队列中删除“self”的逻辑是错误的。您可以从队列中删除
synchronized (queue) {
    queue.add(queuedTask);
}