多线程newSingleThreadExecutor饥饿死锁

问题描述 投票:0回答:3

这直接来自《Java 并发实践》。 以下原因会导致死锁:

class ThreadDeadlock {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    public class RenderPageTask implements Callable<String> {
        public String call() throws Exception {
            Future<String> header, footer;
            System.out.println(Thread.currentThread().getName());
            header = exec.submit(new LoadFileTask("header.html"));
            footer = exec.submit(new LoadFileTask("footer.html"));
            //String page = renderBody();
            // Will deadlock -- task waiting for result of subtask
            //System.out.println("executed header and footer");
            String headerS = header.get();
            System.out.println("retrieved header");
            String footerS = footer.get();
            return headerS+ footerS;
        }
    }
}



class LoadFileTask implements Callable<String>{
    String name;
    public LoadFileTask(String name) {
        super();
        this.name = name;
    }

    @Override
    public String call() throws Exception {
        /*Scanner s = new Scanner(name);
        StringBuilder str = new StringBuilder();
        while(s.hasNextLine()){
            str.append(s.nextLine());
        }*/
        System.out.println(Thread.currentThread().getName()+this.name);
        return "";
    }
}

/* Call from Main */
         private static void checkDeadLock(){
    ThreadDeadlock.RenderPageTask callable = new ThreadDeadlock().new RenderPageTask();
    ExecutorService es = Executors.newFixedThreadPool(1);
    es.submit(callable);
    es.shutdown();
}

我无法弄清楚为什么这会导致僵局。 我检查过了,确实如此。它甚至返回 header.get() 并打印检索到的标头,那么它是如何死锁的?

java multithreading deadlock executors
3个回答
2
投票

代码中不存在死锁,JVM 不会关闭,因为它有工作线程 -

exec
服务的执行器线程(在
ThreadDeadlock
中定义)。 如果你添加

exec.shutdown();

return
中的
RenderPageTask
之前,程序将在所有任务完成后终止。


0
投票

死锁的原因很简单:第一个任务提交了另外 2 个任务并等待它们完成,有效地阻塞了执行器的单个工作线程并阻止任务执行。

要么:

  • 永远不要在执行器下运行的任务中使用阻塞操作

  • 或使用执行器根据需要添加工作线程


0
投票

我对这样的例子产生了误解。 但正如 @ice 所说 - 您应该对页眉/页脚和 RenderPageTask 使用相同的执行器。 这是一个死锁的例子:

public static void main(String[] args) throws Exception {
    var poolDeadlock = new ThreadDeadlock().new RenderPageTask();
    System.out.println(poolDeadlock.call());
    poolDeadlock.getExecutor().shutdown();
}

public static class ThreadDeadlock {
    private final ExecutorService exec = Executors.newSingleThreadExecutor();

    public class RenderPageTask implements Callable<String> {
        public String call() throws Exception {
            Future<String> header, footer;
            header = exec.submit(new LoadFileTask("header.html"));
            footer = exec.submit(new LoadFileTask("footer.html"));
            String page = renderPage();
            // Deadlock because you're using same single thread pool for header/footer
            // and RenderPageTask
            return header.get() + page + footer.get();
        }

        private String renderPage() throws Exception {
            return exec.submit(new RenderPageTask()).get();
        }

        public ExecutorService getExecutor() {
            return exec;
        }
    }
}

public static class LoadFileTask implements Callable<String> {
    private final String data;

    public LoadFileTask(String data) {
        this.data = data;
    }

    @Override
    public String call() {
        return data;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.