2016-11-15 27 views
1

私はExecutorServiceで説明できない動作を観察しています。私は約800人のプロフィールをメモリにロードしているアプリケーションを持っています。以下を考慮Java - ExecutorServiceと明らかにアイドル状態のスレッド

ExecutorService es = Executors.newFixedThreadPool(8); // 8 cores machine 
Runnable enricherService = new Runnable() { 

    @Override 
    public void run() { 
      doEnrichment(conn, tmpCachePerson); 
    } 
}; 

// tmpCachePerson is a ConcurrentLinkedQueue<Person> 
while (tmpCachePerson.isEmpty() == false) { 
    es.execute(enricherService); 
} 

es.shutdown(); 

try { 
    while (!es.awaitTermination(24L, TimeUnit.HOURS)) { 
     System.out.println("Waiting for termination"); 
    } 
} catch (InterruptedException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
} 

このスレッドは、1スレッドのプールに比べて非常に遅いです。私はコードにprintlnを配置し、すべてのスレッドが停止し、16秒までそこに座ってから、バッチのように再開しますが、反復の間に睡眠をとっていることがわかります。完了には50秒かかります。私はその後、以下の実装を試みた:

Runnable enricher2Thread = new Runnable() { 

      @Override 
      public void run() { 
       while (tmpCachePerson.isEmpty() == false) { 
        doEnrichment(conn, tmpCachePerson); 
       } 
      } 
     }; 

     Thread t = new Thread(enricher2Thread); 
     t.start(); 
     try { 
      t.join(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

一方、この作品を、コンソールに印刷を停止せず、3Sに完了しない、非常に高速であるだけで1つのスレッドを使用します。

最初の部分の固定プールをキャッシュプールに置き換えると、タスクは800個のスレッドを生成した後、3秒で完了します。固定プールに800スレッドを置くと同じスピードです。なぜ誰かが固定プールが毎回頻繁に休止し、なぜ1つのスレッドより高速でないのか理解しています。以下は私が8つのスレッドで見るものの抜粋です。スレッド1を見ると、単純なゲッターで5秒間一時停止します。ログの他の部分では、タスク全体が約250msのタクキーになります。

2016-11-15 15:54:04.212 - pool-1-thread-1 - Starting 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Starting executing SQL 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Done executing SQL in 0 ms 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Starting adding 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Starting Getting Val 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Done Getting Val in 0 ms 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Starting Getting Root 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Done Getting Root in 0 ms 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Starting Getting Path 
2016-11-15 15:54:04.212 - pool-1-thread-1 - Done Getting Path in 0 ms 
2016-11-15 15:54:04.212 - pool-1-thread-6 - Starting 
2016-11-15 15:54:04.212 - pool-1-thread-8 - Starting <-------------- All threads stop 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Starting executing SQL 
2016-11-15 15:54:09.533 - pool-1-thread-6 - Starting executing SQL 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Done executing SQL in 0 ms 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Starting Getting Full Path 
2016-11-15 15:54:09.533 - pool-1-thread-6 - Done executing SQL in 5320 ms 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Starting adding 
2016-11-15 15:54:09.533 - pool-1-thread-6 - Starting adding 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Starting Getting Val 
2016-11-15 15:54:09.533 - pool-1-thread-6 - Starting Getting Val 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Done Getting Val in 0 ms 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Starting Getting Root 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Done Getting Full Path in 5320 ms 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Starting Adding Image 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Done Getting Root in 0 ms 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Done Adding Image in 0 ms 
2016-11-15 15:54:09.533 - pool-1-thread-8 - Starting Getting Path 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Done adding in 5321 ms 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Starting setting 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Done setting in 0 ms 
2016-11-15 15:54:09.533 - pool-1-thread-1 - Done in 5321 ms 

私はこのコードをどのように改善でき、なぜそれが一時停止するのでしょうか?ここでは、次のとおりです:

private void doEnrichment(Connection conn, ConcurrentLinkedQueue<Person> tmpCachePerson) { 
     Person person = tmpCachePerson.poll(); 

     if (person != null) { 
      ImageCollection personImageCollection; 
      String query = "SELECT epi.value, i.path FROM Image i " 
        + "INNER JOIN EntityImageRelationship eir ON eir.id_image = i.id " 
        + "INNER JOIN EntityType et ON eir.id_entity_type = et.id " 
        + "INNER JOIN EntityPrimaryImage epi ON epi.type_to_entity_uid = eir.type_to_entity_uid " 
        + "WHERE et.id = ? AND eir.id_entity_id = ? ORDER BY i.id ASC"; 

      String tagQuery = "SELECT id, value FROM Tag t INNER JOIN EntityTagRelationship etr ON etr.id_tag = t.id WHERE etr.id_entity = ? AND etr.id_entity_type = ?"; 

      try (PreparedStatement stmnt = conn.prepareStatement(query); 
        PreparedStatement tagStmnt = conn.prepareStatement(tagQuery)) { 
       personImageCollection = getEntityImages(conn, stmnt, person.getId(), person.getMovieEntityType()); 
       person.setImageCollection(personImageCollection); 
       person.getImageCollection().setPrimaryImageIcon(); 

       Set<String> tags = getEntityTags(conn, tagStmnt, person.getId(), person.getMovieEntityType()).keySet(); 
       person.setTags(tags); 

       personCache.add(person); 
      } catch (Exception e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 
+1

で労働者を起動します。

より良いパターンは、労働者にポーリングを置くことです完了?そうであれば、先物を使用し、get(long timeout、TimeUnit unit)を呼び出します。 – joseph

+0

はい、良い提案です。私はコードが生産を打つ頃にこれを行うでしょう。 – user292272

答えて

1

これが原因ではないかもしれませんが、あなたはここで競合状態を持っている:

while (tmpCachePerson.isEmpty() == false) { 
    es.execute(enricherService); 
} 
それは

編集を助けている場合、私は)(doEnrichmentのコードを投稿することができます

コンテキスト切り替えが発生することは絶対に保証されません。たとえそれがあっても、予想よりもおそらく遅くなります。このループは、作業者が起動する前に100万回実行される可能性があります。あなたがこの問題を解決するまで、それ以上見てはいけません。それは回転とメモリのオーバーヘッドがたくさんあります。

Runnable enricherService = new Runnable() { 
    @Override 
    public void run() { 
     while (!tmpCachePerson.isEmpty()) { 
      doEnrichment(conn, tmpCachePerson); 
      // TODO: error handling? Should a failure in doEnrichment kill the worker? 
     } 
    } 
}; 

そしてあなたはdoEnrichmentのランナブルがある場合を決定するために、シャットダウンとawaitTerminationを使用してい

for (int i = 0; i < 8; ++i) { 
    es.execute(enricherService); 
} 
+0

これは約4.5秒で実行されます。 1つのスレッドよりも1秒多く、私が持っていたよりも優れています。私は後になっているが解決策はありません。ありがとうございます – user292272

+0

あなたはデータベースを照会しています。どのようにあなたの接続を取得していますか?プールされていないと、パフォーマンスの問題が発生する可能性があります。プールが小さすぎる場合(<8)、スレッドはブロックされ、接続を待機します。あなたの接続に 'System.identityHashCode()'を出力してみてください。理想的には、少なくとも8個(800個未満)のIDが表示されます。あなたは1(または800)を取得する場合は、プールを使用する必要があります。あなたが〜4を得るなら、大きなプールが必要です。 –

+0

これは、すべてのスレッドに対して単一の接続を使用しているか、DBがボトルネックになっているような気分ですが、おそらく接続です。 –

関連する問題