2016-11-29 21 views
0

現在、これを行う正しい方法を見つけるのに問題があります。スレッドを必要とするメソッドによってデッドロックが発生する

私は、固定スレッドプールが64のExecutorServiceを持っています。一種のブック(一度に1つ)をダウンロードするよう依頼しています。私が必要とする本をダウンロードするには:書籍情報をダウンロードし、ページ情報をダウンロードして、書籍の一部をダウンロードします。私が本をダウンロードするように要求するとき、私はすべてのページ情報を得て、同じ方法で私はその本の小さな部分をダウンロードする。問題は、書籍のこれらの小さな部分をダウンロードすることも非同期的に(別のスレッドが必要です)実行されていますが、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: Requesting to dl page 10753>67 eId235082 eId235092 それは(技術的に実行されているが、何もしていない)を停止した後

私が(デバッガを使用して)、スレッドを中断すると、スタックトレースは#getPageを指し、正確にはlatch.await()になります。

+0

コードが必要です。実際にネットワークにアクセスする必要のない、自己完結型のものをお選びください。それは、なぜこれが「デッドロック」するのか、または飢えているのかわかりません。タスクを完了したら、新しいタスクのためにスレッドを使用できるようにする必要があります。 – markspace

+0

@Kayamanはコード – Mibac

+0

を追加しました。@markspaceしかし、タスクは実際に完了するために別のスレッドが必要です – Mibac

答えて

2

2つの異なる種類のタスクを実行しているため、2つ目のタスクは最初のタスクのサブタスクですから、実行者は最初のタスクがいっぱいです。サブタスクは、実行する。これはデッドロックの古典的な例ではありませんが、私はそれが適格であると言います。

私がこれを処理する方法は、getPage()でエグゼキュータの使用を取り除くことです。何らかの理由で(有効な理由がわからなくても)複数のスレッドを使用してgetPage()を保持したい場合は、別のExecutorを使用する必要があるため、サブタスクには常に終了するチャンスがあります。

+0

ありがとう!私はあなたの助けに感謝します – Mibac

+2

これも 'ForkJoinTask'の古典的なケースではありませんか? – biziclop

+0

@biziclopそうです、それはもっとクリーンなアプローチでしょう。 – Kayaman

0

あなたが報告しているデッドロックではありません。あなたはスレッドを使い果たしています。

あなたのスレッドは多くのI/O作業を行っています(これは問題ありません)が、これらの接続を閉じていないと、タスクが完了していない可能性があり、ThreadPoolが他のタスク用のスレッド。

更新:お互いに依存するスレッドがあります。一般に、これは悪い考えです。おそらくあなたがしたいことは、処理パイプラインを作成することです。 1つの部分を行い、結果をキューに入れます。要求を完了するためにキューを読み取るための別のエグゼキュータ・サービスを依頼してください。

+0

私はスレッドがなくなっていることを知っていますが、接続を終了していますが、タスクを完了するためには別のスレッドが完了する必要があります。それは主要な問題です - それをどう扱うか – Mibac

関連する問題