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

4

3 回答 3

12

死锁和饥饿发生在以下行:

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. 任务通过已实现的类提交给exec渲染页面。CallableRenderPageTask
  2. exec启动时是RenderPageTask分开Thread的,唯一的就是执行顺序Thread提交的其他任务。exec
  3. 里面还有两个任务的call()方法被提交到。第一个是,第二个是。但是由于通过此处提到的代码获得的 ExecutorService使用单个工作线程操作无界 queueThread并且该线程已分配给 RenderPageTask ,因此将 排队到无界队列等待轮流执行。RenderPageTaskexecLoadFileTask("header.html")LoadFileTask("footer.html")execExecutors.newSingleThreadExecutor(); LoadFileTask("header.html")LoadFileTask("footer.html")Thread
  4. RenderPageTask正在返回一个字符串,其中包含 的输出 LoadFileTask("header.html")、页面正文和 的输出的串联LoadFileTask("footer.html")。这三个部分page是由 成功获得的RenderPageTask。但是其他两个部分只有在两个任务都由分配的单个线程执行后才能获得ExecutorServicecall()并且线程只有在返回方法之后才会被释放RenderPageTask。但是call方法 ofRenderPageTask只会在LoadFileTask("header.html")andLoadFileTask("footer.html")被返回之后返回。所以不让LoadFileTask执行会导致饥饿。并且每个等待其他任务完成的任务都会导致死锁

    我希望这可以说明为什么上面代码中会发生线程饥饿死锁。
于 2013-02-02T13:19:53.967 回答
0

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

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

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

于 2013-02-02T10:49:36.170 回答
0

原因从代码本身看不是很明显,而是从复制代码的原书来看:RenderPageTask向Executor提交了两个额外的任务以获取页面页眉和页脚......

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

于 2018-02-02T21:30:16.933 回答