代码中是否发生了线程匮乏死锁


//code taken from java concurrency in practice
  package net.jcip.examples;
import java.util.concurrent.*;

public class ThreadDeadlock
       {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    public class LoadFileTask implements Callable<String> {
        private final String fileName;
        public LoadFileTask(String fileName) {
            this.fileName = fileName;
        }
        public String call() throws Exception {
            // Here's where we would actually read the file
            return "";
        }
    }
    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 = renderBody();
            // Will deadlock -- task waiting for result of subtask
            return header.get() + page + footer.get();
        }

    }
}

这段代码在实践中取自Java并发,根据作者的说法,"ThreadStarvtionDeadlock"在这里发生。请帮我找到ThreadStarvationDeadlock在这里和哪里发生的方式?提前谢谢。

锁和饥饿发生在以下行:

return header.get() + page + footer.get();

如何?
如果我们向程序添加一些额外的代码,就会发生这种情况。可能是这个:

    public void startThreadDeadlock() throws Exception
    {
        Future <String> wholePage = exec.submit(new RenderPageTask());
        System.out.println("Content of whole page is " + wholePage.get());
    }
    public static void main(String[] st)throws Exception
    {
        ThreadDeadLock tdl = new ThreadDeadLock();
        tdl.startThreadDeadLock();
    }

导致死锁的步骤:

  1. 任务通过实现的类RenderPageTask提交到exec以呈现页面Callable
  2. exec以单独的Thread启动RenderPageTask,这是唯一可以按顺序执行提交给exec的其他任务的Thread
  3. call() RenderPageTask方法中,另外两个任务被提交给exec。第一个是LoadFileTask("header.html"),第二个是LoadFileTask("footer.html")。但是由于这里提到的通过代码Executors.newSingleThreadExecutor();获得的 ExecutorService exec使用单个工作线程在无界 queueThread 上运行,并且该线程已经分配给 RenderPageTask,因此LoadFileTask("header.html")LoadFileTask("footer.html")将被排队到无界队列中等待该Thread执行。
  4. RenderPageTask返回一个字符串,其中包含LoadFileTask("header.html")输出、页面正文和LoadFileTask("footer.html")输出的串联。在这三个部分中,page通过RenderPageTask成功获得。但其他两个部分只有在两个任务都由ExecutorService分配的单个线程执行后才能获得。只有在call() RenderPageTask返回的方法之后,线程才会免费。但是call RenderPageTask方法只有在LoadFileTask("header.html")和返回LoadFileTask("footer.html")后才会返回。所以不让LoadFileTask执行会导致饥饿。每个等待其他任务完成的任务都会导致死锁

    我希望这能清楚地说明为什么上面的代码中会出现线程匮乏死锁。

我看到的执行器是一个单线程执行器,它有两个任务要做。但是,这两个任务并不相互依赖,它们的执行顺序似乎并不重要。因此,return 语句只会在 Future.get 调用中暂停,以完成一个任务,然后完成另一个任务。

您显示的代码不会死锁。

但是,我在代码中看到另一个任务(RenderPageTask),不清楚哪个执行器实际运行其代码。如果是同一个单线程执行器,则可能出现死锁,因为在主任务返回之前无法处理两个提交的任务(并且此任务只能在处理完两个任务后返回)。

原因不是很明显,从代码本身,而是从代码复制的原始书籍中: RenderPageTask 向执行器提交两个额外的任务来获取页面页眉和页脚......

如果 RenderPageTask 是独立于 newSingleThreadExecutor 的任务,则根本不会有死锁。

最新更新