java - 需要线程的方法导致的死锁

标签 java multithreading download

我目前无法找出执行此操作的正确方法。

我有一个固定线程池为 64 的 ExecutorService。我请求下载一本书(一次一本书)。要下载一本书,我需要:下载书籍信息,下载页面信息,然后下载书籍的一部分。当我请求下载一本书时,我会获取每一页信息,并以相同的方法下载这本书的那些小部分。问题是下载书籍的这些小部分也是异步完成的(需要另一个线程),但当时所有 64 个线程都被页面下载线程占用。我想出了要么添加另一个 ExecutorService 要么将线程池提升到更大的数字,例如 256。但这感觉不太对劲。我还有其他选择吗?

步骤摘要和问题所在位置:

  1. 下载图书信息
  2. 下载页面:

    • 页面信息
    • 逐页显示 -- 死锁 - 线程不足。

      @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:请求dl页面10753>67 电子 ID235082 eId235092 之后它停止(技术上正在运行,但不执行任何操作)

当我中断线程(使用调试器)时,堆栈跟踪指向#getPage,更准确地说是latch.await()

最佳答案

由于您正在执行两种不同类型的任务,而第二个任务是第一个任务的子任务,因此执行器最终会执行第一个任务,而第一个任务无法完成,因为它们的子任务无法完成执行。虽然这不是典型的死锁示例,但我认为它符合条件。

我处理这个问题的方法是删除 getPage() 中执行程序的使用。如果出于某种原因(尽管我没有看到任何有效的原因)您希望/需要使用多个线程保留 getPage(),则必须提供单独的 Executor 供其使用,因此子任务始终有机会完成。

关于java - 需要线程的方法导致的死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40870279/

相关文章:

java - 使用 ArrayList#trimToSize() 方法?

java - 即使兼容类型可用,方法重载是否隐式类型转换为更大的类型

android - 如何检查我的应用程序是否可以从谷歌播放下载

javascript - 如果文件或文件位置都不包含,则设置文件名和格式

java - 如何通过蓝牙向未指定的设备发送串行数据?

java - Struts 1.1 + Hibernate 3.2 集成

c++ - 是否可以使用互斥锁来锁定 vector 中的元素而不是整个 vector ?

python - 为什么在 python 线程中监视键盘中断不起作用

c++ - 我的多线程程序在双核机器上运行缓慢或出现死锁,请帮忙

c# - Visual Studio 模板