2

複数のスレッドが自分自身を登録するマネージャクラスがあります(要求ごとに一意の識別子を生成するためにUUIDを使用します)。マネージャー。私はjava.util.concurrent.ExecutorServiceを使って複数のスレッドを起動しています。ここではここJava ExecutorService: - イベントが発生したときに起動するスレッドに通知する

public class ManagerTest { 
    public static void main(String[] args) { 
     try { 
      Manager myManager = new Manager(); 
      // Start listening to the messages from different threads 
      myManager.consumeMessages(); 
      int num_threads = Integer.parseInt(args[0]); 
      ExecutorService executor = Executors.newFixedThreadPool(num_threads); 

      for (int i = 0; i < num_threads; i++) { 
       // class implementation is given below 
       Runnable worker = new MyRunnable(myManager); 
       executor.execute(worker); 
      } 
      executor.shutdown(); 
      // Wait until all threads are finish 
      while (!executor.isTerminated()) { 

      } 
      System.out.println("\nFinished all threads"); 

      myManager.closeConnection(); 

     } catch (IOException | TimeoutException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

functionality-私のマネージャーをテストするための実装はMyRunnableクラスの実装は、管理者が要求を処理し、ペイロードに応じて、要求の応答は、様々な量を取ることができ

class MyRunnable implements Runnable { 
    private Manager managerObj; 
    public MyRunnable(Manager managerObj) { 
     this.managerObj = managerObj; 
    } 

    @Override 
    public void run() {  
     try { 
      Random rand = new Random(); 
      int n = rand.nextInt(35); 
      String requestId = UUID.randomUUID().toString(); 
      managerObj.registerRequest(requestId, n); 
      managerObj.publishMessage(requestId); 
      // Want to avoid this while loop 
      while(! managerObj.getRequestStatus(requestId)){ 

      } 
      int response = managerObj.getRequestResponse(requestId); 
      // do something else 
      managerObj.unregisterRequest(requestId); 

     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

です時間の。マネージャが応答を得るたびに、この関数を呼び出すことによって要求ステータスをtrueに設定します。setRequestStatus(requestId)。その後、スレッドはwhile loopから終了し、実行を継続します。

コードは正常に動作していますが、条件が満たされるまでwhileループを連続的にループすることによってスレッドがあまりにも多くの作業をしています。

マネージャにリクエストを送信した後にスレッドをスリープさせる方法です。マネージャは、レスポンスの準備ができたらこのスレッドに信号を送ります。

私はこれが誰かにはあまりにも単純な音なら、私はjavaとjava-threadingインターフェイスの初心者です。

+0

'executor.isTerminated()'の代わりに 'ExecutorService.awaitTermination'メソッドを使用してください。詳細については、ExecutorServiceのjavadocを参照してください。 –

答えて

3

私たちは簡単な質問に慣れています。あなたの質問は実際によく書かれており、解決するのに合理的な問題があります。毎日、彼らが何を望んでいるのか分からないように、それで、あなたがやっているのはビジー・スピン・ループです。これは非常に悪いことです。なぜなら、a)スレッドごとにCPUコアを完全に消費し、b)実際にはCPUがビジーです。これは、処理に時間がかかる他のスレッドから盗み出していることを意味します。

これを解決するにはいくつかの方法がありますが、私はそれらを最悪から最高の順に表示します。

  • あなたのコードを改善するための最も簡単な方法は、パラメータとして渡す0java.lang.Thread.sleep(long millis)メソッドを呼び出すことです。これは、「yield」オペレーションとも呼ばれ、「実行するいくつかの有益な作業がある他のスレッドがあれば、それらを実行して、完了したら私に戻ってきます」ということを本質的に意味します。これは100%CPUを消費するため、ビジー・スピン・ループよりもわずかに優れています。利点は、他のスレッドは何もしていない間にCPUを消費するだけなので、少なくとも他のスレッドは遅くならないということです。

  • コードを改善する次善の方法は、1をパラメータとして使用してjava.lang.Thread.sleep(long millis)メソッドを呼び出すことです。これは「パス」操作と呼ばれ、「タイムスライスの残りの部分を他のスレッドに解放する」という意味です。言い換えれば、タイムスライスの残りの部分は失われます。たとえシステム全体で有用な作業を行う必要がないとしてもです。これにより、CPU消費量はほぼゼロになります。欠点は、a)CPU消費量が実際にはわずかにゼロを上回ること、b)マシンが何らかの低電力スリープモードに移行できないこと、c)ワーカースレッドがわずかに応答性が低いこと、タイムスライスの境界でのみ動作します。この回答で説明したように

  • あなたの問題を解決する最良の方法は、Javaのに組み込まれて同期メカニズムを使用することです:https://stackoverflow.com/a/5999146/773113はこれだけにゼロCPUを消費しませんが、それも機械が入るようになります待機中に低電力モード。

  • 最も一般的な場合の問題を解決するには、条件が成立するまで待つのではなく、実行する作業についての情報も渡す必要がある場合は、BlockingQueueを使用します。 (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html)ブロッキングキューは、Java組み込み同期メカニズムを使用して動作し、あるスレッドが別のスレッドに情報を渡すことを許可します。

+0

アクタを使用して、および/または反応的な方法(CompletableFuture)を実行することで、プログラミングプログラミングのパラダイムについて言及することができます。とにかくニースの答え。 @GPIありがとうございます。 – GPI

+0

私は、既存の 'while()'ループでいくつかのコードを埋め込むだけで、問題を解決する可能性があるときに、別のプログラミング・パラダイムを使用するよう提案するのは少し難しいかもしれないと思います。 (私は、 'BlockingQueue'についての提案は、すでに過小評価されていると思っています。)しかし、教育のために、それは傷つけません。問題は、私はそれについての快適な文章を感じるために反応するための十分な露出がないということです。だから、それを自由にしてください。 –

0

すでに@MikeNakisの素晴らしい回答がありますが、私は別のオプションも提供したいと考えています。

このオプションでは、Futureを返すようにManager APIを変更します。

  • が代わりにこれらの変更MyRunnableさんに変更することができますrun()方法でgetRequestResponse()リターンFuture<Integer>

を作るgetRequestStatus()方法をドロップ:私は提案するマネージャへ

変更はこれらです:

public void run() {  
    try { 
     Random rand = new Random(); 
     int n = rand.nextInt(35); 
     String requestId = UUID.randomUUID().toString(); 
     managerObj.registerRequest(requestId, n); 
     managerObj.publishMessage(requestId); 

     // Future.get() blocks and waits for the result without consuming CPU 
     int response = managerObj.getRequestResponse(requestId).get(); 
     // do something else 
     managerObj.unregisterRequest(requestId); 

    } catch (IOException e) { 
     e.printStackTrace(); 
    } catch (InterruptedException e) { 
     Thread.currentThread().interrupt(); 
    } 
} 

Future<>を実装する最も簡単な方法は、おそらくJavaのjava.util.concurrent.FutureTaskを使用することです。

関連する問題