这是《Java并发编程实践》中的8.1列表:
public class ThreadDeadlock {
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 = renderBody();
//Will deadlock -- task waiting for result of subtask
return header.get() + page + footer.get();
}
}
}
这在第8章:线程池 > 第8.1.1节 线程饥饿死锁 中提到,并有标题:
"任务在单线程
Executor
中发生死锁。 不要这样做。"
为什么会导致死锁?我认为应该先调用header.get()
,然后调用footer.get()
,每个结果都追加到字符串中。为什么单线程 Executor 不足以一个接着一个地运行它们?
相关章节内容:
8.1.1 线程饥饿死锁
如果依赖于其他任务的任务在线程池中执行,则可能会发生死锁。在单线程执行器中,提交另一个任务到同一执行器并等待其结果的任务将始终死锁。第二个任务坐在工作队列上,直到第一个任务完成,但是第一个任务不会完成,因为它正在等待第二个任务的结果。如果所有线程都在执行被阻塞等待其他仍在工作队列中的任务的任务,则较大的线程池中也可能发生这种情况。这称为线程饥饿死锁,并且只要池任务启动了对某些资源或条件的无限制阻塞等待,该资源或条件只能通过另一个池任务的操作成功,例如等待另一个任务的返回值或副作用,除非您可以保证池足够大。
清单8.1中的
ThreadDeadlock
说明了线程饥饿死锁。RenderPageTask
提交两个附加任务以获取页面页眉和页脚,渲染页面正文,等待页眉和页脚任务的结果,然后将页眉、正文和页脚组合成最终页面。在单线程执行器中,ThreadDeadlock
将始终死锁。同样,如果池不够大,使用屏障协调彼此的任务也可能导致线程饥饿死锁。
LoadFileTask
做什么? - Sotirios DelimanolisLoadFileTask
?这使得分析问题或解释为什么会死锁变得困难。虽然这段代码片段不包含任何可执行内容,因此没有任何东西可以死锁,因为没有任何东西可以运行。 - RDMLoadFileTask
是一个虚构任务。我将添加这一章节的文本... - aliteralmind