我目前无法找到正确的方法。
我有一个固定线程池为64的ExecutorService。我正在请求下载一种Book(一次一个)。要下载我需要的书:下载书籍信息,下载页面信息,然后下载书的一部分。当我要求下载一本书时,我得到了每一页信息,并以同样的方法下载了本书的那些小部分。问题是下载书籍的那些小部分也是异步完成的(需要另一个线程),但当时所有64个线程都被页面下载线程占用。我想出了添加另一个ExecutorService或将线程池提升到更大的数字,如256.但这感觉不太对劲。我还有其他选择吗?
问题的步骤和位置摘要:
下载页面:
页面的部分内容 - 死锁 - 超出线程。
@Override
public Book getBook(int bookId) {
Book book = books.get(bookId);
if (book == null) {
HttpURLConnection conn = factory.getBook(bookId);
String s = read(conn);
book = interpret.readBook(s);
books.put(book.getId(), book);
}
return book;
}
@Override
public Page getPage(int bookId, int pageNum) {
String s = read(factory.getPage(bookId, pageNum));
List<Integer> eIds = interpret.readExercises(s);
List<Exercise> exercises = new ArrayList<>(eIds.size());
CountDownLatch latch = new CountDownLatch(eIds.size());
System.out.println("D: Requesting to dl page " + bookId + '>' + pageNum);
for (int eId : eIds) {
System.out.println("eId" + eId);
service.submit(() -> {
try {
// The code here does not execute to the lack of free threads
System.out.println("D: Requesting to dl exer " + eId);
String sE = read(factory.getExercise(bookId, eId));
Exercise exercise = interpret.readExercise(sE);
exercises.add(exercise);
latch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Page(pageNum, exercises);
}
@Override
public WholeBook getWholeBook(int bookId) {
Book book = getBook(bookId);
List<Page> pages = new ArrayList<>(book.getPages().size());
CountDownLatch latch = new CountDownLatch(book.getPages().size());
System.out.println("D: Requesting to dl book " + bookId);
for (int pageNum : book.getPages()) {
service.submit(() -> {
try {
Page page = getPage(bookId, pageNum);
System.out.println("Got page: " + page);
pages.add(page);
latch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
});
}
try {
System.out.println("Waiting for book " + bookId);
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
return null; // Better to return null rather than corrupted data
}
return new WholeBook(book, pages);
}
输出的结尾是:
D: Requesting to dl page 10753>67
eId235082
eId235092
之后它停止(技术上运行但没有做任何事情)
当我中断线程(使用调试器)时,堆栈跟踪点指向#getPage,并且更精确地指向latch.await()
。
答案 0 :(得分:2)
由于您正在执行两种不同类型的任务,而第二项是第一项任务的子任务,因此您最终会让执行程序充满第一项任务,这些任务自完成以来无法完成子任务无法执行。虽然这不是死锁的典型例子,但我认为它符合条件。
我处理这个问题的方法是删除getPage()
中执行程序的使用。如果由于某种原因(虽然我没有看到任何正当理由)您希望/需要使用多个线程保持getPage()
,您必须提供单独的Executor
才能使用,所以子任务总是有机会完成。
答案 1 :(得分:0)
从技术上讲,这不是你报告的死锁。你的线程用完了。
看起来你的线程正在进行大量的I / O工作(这很好),但如果你没有关闭这些连接,那么任务可能没有完成,并且ThreadPool无法重新分配线程其他任务。
更新:我知道,你有相互依赖的线程。总的来说这是个坏主意。您可能想要做的是创建一个处理管道。做一个部分,将结果放入队列中。有另一个执行器服务来读取队列以完成请求。