代码中是否发生线程饥饿死锁?

时间:2013-02-02 10:41:22

标签: java concurrency deadlock executorservice

//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是如何在这里发生的?提前谢谢。

3 个答案:

答案 0 :(得分:12)

死锁&amp;饥饿发生在以下几行:

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

导致deadLock的步骤:

  1. 任务已提交至exec,以便通过Callable已实施的课程RenderPageTask呈现该网页。
  2. exec在单独的RenderPageTask中启动了ThreadThread是执行其他任务的exec,后续执行call()
  3. RenderPageTaskexec方法,另外两个任务提交给LoadFileTask("header.html")。首先是LoadFileTask("footer.html"),第二个是exec。但是由于提到的Executors.newSingleThreadExecutor();通过代码LoadFileTask("header.html")获得的here 使用单个工作线程操作无限制的queueThread ,并且该线程已经分配给RenderPageTask ,因此LoadFileTask("footer.html")Thread将排队到无限队列,等待由RenderPageTask执行。
  4. LoadFileTask("header.html")返回一个字符串,其中包含LoadFileTask("footer.html")的输出,页面主体和page输出的串联。 RenderPageTask成功获得了这三个部分ExecutorService。但是,只有在call()分配的单个线程执行这两个任务后才能获得其他两个部分。只有在RenderPageTask call方法返回后,线程才会免费。但RenderPageTask的{​​{1}}方法只会在LoadFileTask("header.html")LoadFileTask("footer.html")返回后返回。所以不要让LoadFileTask执行导致饥饿。等待完成其他任务的每个任务都会导致 DeadLock

    我希望这清楚地解释了为什么在上面的代码中发生线程饥饿死锁。

答案 1 :(得分:0)

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

您显示的代码不会出现死锁。

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

答案 2 :(得分:0)

原因不是很明显来自代码本身,而是来自复制代码的原始书:RenderPageTask向Executor提交两个附加任务以获取页眉和页脚...

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