web-dev-qa-db-ja.com

Javaスレッドを別のスレッドの出力を待機させる方法は?

Application-logic-threadとdatabase-access-threadでJavaアプリケーションを作成しています。両方ともアプリケーションの存続期間全体にわたって持続し、両方を同時に実行する必要があります(1つはサーバーと通信し、1つはユーザーと通信します。アプリが完全に起動したら、both =動作するように)。

ただし、起動時に、最初にアプリスレッドがdbスレッドの準備ができるまで待機することを確認する必要があります(現在、カスタムメソッドdbthread.isReady()をポーリングすることで決定されます)。 dbスレッドの準備ができるまでアプリスレッドがブロックされるかどうかは気にしません。

Thread.join()は解決策のようには見えません-dbスレッドはアプリのシャットダウン時にのみ終了します。

while (!dbthread.isReady()) {}の種類は動作しますが、空のループは多くのプロセッササイクルを消費します。

他のアイデアはありますか?ありがとう。

121
Piskvor

マルチスレッドの魔法の世界を始める前に、 SunのJava Concurrency のようなチュートリアルを読むことをお勧めします。

優れた書籍も多数あります(「Javaでの並行プログラミング」、「Java並行性の実践」のグーグル)。

答えを得るには:

dbThreadを待つ必要があるコードでは、次のようなものが必要です。

//do some work
synchronized(objectYouNeedToLockOn){
    while (!dbThread.isReady()){
        objectYouNeedToLockOn.wait();
    }
}
//continue with work after dbThread is ready

dbThreadのメソッドでは、次のようにする必要があります。

//do db work
synchronized(objectYouNeedToLockOn){
    //set ready flag to true (so isReady returns true)
    ready = true;
    objectYouNeedToLockOn.notifyAll();
}
//end thread run method here

これらの例で使用しているobjectYouNeedToLockOnは、各スレッドから同時に操作する必要があるオブジェクトであるか、その目的のために別個のObjectを作成できます(メソッド自体を作成することはお勧めしません) )同期:

private final Object lock = new Object();
//now use lock in your synchronized blocks

理解を深めるために:
上記を実行する方法は他にもあります(場合によってはより良い)。 CountdownLatchesなど。Java 5以降、Java.util.concurrentパッケージとサブパッケージには多くの気の利いた同時実行クラスがあります。並行性を知るため、または良い本を入手するために、オンラインで資料を見つける必要があります。

126
Herman Lintvelt

CountDownLatch を1のカウンターで使用します。

CountDownLatch latch = new CountDownLatch(1);

今アプリスレッドで

latch.await();

Dbスレッドで、完了したら-

latch.countDown();
132
pdeva

要件::

  1. 前のスレッドが終了するまで、次のスレッドの実行を待機します。
  2. 次のスレッドは、時間の消費に関係なく、前のスレッドが停止するまで開始してはなりません。
  3. シンプルで使いやすいものでなければなりません。

回答::

@See Java.util.concurrent.Future.get()doc。

future.get()計算が完了するまで必要に応じて待機し、その結果を取得します。

仕事完了!!以下の例を参照してください

import Java.util.concurrent.Callable;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;

import org.junit.Test;

public class ThreadTest {

    public void print(String m) {
        System.out.println(m);
    }

    public class One implements Callable<Integer> {

        public Integer call() throws Exception {
            print("One...");
            Thread.sleep(6000);
            print("One!!");
            return 100;
        }
    }

    public class Two implements Callable<String> {

        public String call() throws Exception {
            print("Two...");
            Thread.sleep(1000);
            print("Two!!");
            return "Done";
        }
    }

    public class Three implements Callable<Boolean> {

        public Boolean call() throws Exception {
            print("Three...");
            Thread.sleep(2000);
            print("Three!!");
            return true;
        }
    }

    /**
     * @See Java.util.concurrent.Future.get() doc
     *      <p>
     *      Waits if necessary for the computation to complete, and then
     *      retrieves its result.
     */
    @Test
    public void poolRun() throws InterruptedException, ExecutionException {
        int n = 3;
        // Build a fixed number of thread pool
        ExecutorService pool = Executors.newFixedThreadPool(n);
        // Wait until One finishes it's task.
        pool.submit(new One()).get();
        // Wait until Two finishes it's task.
        pool.submit(new Two()).get();
        // Wait until Three finishes it's task.
        pool.submit(new Three()).get();
        pool.shutdown();
    }
}

このプログラムの出力::

One...
One!!
Two...
Two!!
Three...
Three!!

他のスレッドよりも大きいタスクを完了するまでに6秒かかります。したがって、Future.get()はタスクが完了するまで待機します。

Future.get()を使用しない場合、終了するのを待たずに、ベースの時間消費が実行されます。

Java同時実行の幸運。

22
Ash
public class ThreadEvent {

    private final Object lock = new Object();

    public void signal() {
        synchronized (lock) {
            lock.notify();
        }
    }

    public void await() throws InterruptedException {
        synchronized (lock) {
            lock.wait();
        }
    }
}

このクラスを次のように使用します:

ThreadEventを作成します。

ThreadEvent resultsReady = new ThreadEvent();

メソッドでは、結果を待っています:

resultsReady.await();

そして、すべての結果が作成された後に結果を作成するメソッドでは:

resultsReady.signal();

編集:

(この投稿を編集して申し訳ありませんが、このコードは非常に悪い競合状態にあり、コメントするのに十分な評判がありません)

これは、await()の後にsignal()が確実に呼び出される場合にのみ使用できます。これがJavaオブジェクトを使用できない大きな理由の1つです。 Windowsイベント。

コードが次の順序で実行される場合:

Thread 1: resultsReady.signal();
Thread 2: resultsReady.await();

スレッド2は永遠に待機します。これは、Object.notify()が現在実行中のスレッドの1つのみを起動するためです。後で待機しているスレッドは起こされません。これは、a)待機するか、b)明示的にリセットするまでイベントが通知されるイベントの動作とはまったく異なります。

注:ほとんどの場合、notifyAll()を使用する必要がありますが、これは上記の「永遠に待機する」問題には関係ありません。

8
Lourens Coetzer

CountDownLatchJava.util.concurrentパッケージのクラスを試してください。これは、高レベルの同期メカニズムを提供し、低レベルのものよりもエラーがはるかに少ないです。

7
WMR

正解はたくさんありますが、簡単な例はありません。CountDownLatchの簡単で簡単な使用方法は次のとおりです。

//inside your currentThread.. lets call it Thread_Main
//1
final CountDownLatch latch = new CountDownLatch(1);

//2
// launch thread#2
new Thread(new Runnable() {
    @Override
    public void run() {
        //4
        //do your logic here in thread#2

        //then release the lock
        //5
        latch.countDown();
    }
}).start();

try {
    //3 this method will block the thread of latch untill its released later from thread#2
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

//6
// You reach here after  latch.countDown() is called from thread#2
6
Maher Abuthraa

2つのスレッド間で共有される Exchanger オブジェクトを使用して実行できます。

private Exchanger<String> myDataExchanger = new Exchanger<String>();

// Wait for thread's output
String data;
try {
  data = myDataExchanger.exchange("");
} catch (InterruptedException e1) {
  // Handle Exceptions
}

そして、2番目のスレッドで:

try {
    myDataExchanger.exchange(data)
} catch (InterruptedException e) {

}

他の人が言ったように、この気楽なだけのコードをコピー&ペーストしないでください。最初に読んでください。

6
kgiannakakis

Java.lang.concurrentパッケージの Future インターフェイスは、別のスレッドで計算された結果へのアクセスを提供するように設計されています。

FutureTaskExecutorService を見て、この種のことを行う既製の方法を確認してください。

Java Concurrency In Practice を並行処理とマルチスレッドに興味のある方には強くお勧めします。明らかにJavaに焦点を当てていますが、他の言語で働いている人にとっても多くの肉があります。

4
Bill Michell

これはすべての言語に適用されます。

イベント/リスナーモデルが必要です。特定のイベントを待機するリスナーを作成します。イベントはワーカースレッドで作成(または通知)されます。これにより、現在のソリューションのように、常にポーリングして条件が満たされているかどうかを確認する代わりに、信号を受信するまでスレッドがブロックされます。

状況は、デッドロックの最も一般的な原因の1つです。発生した可能性のあるエラーに関係なく、他のスレッドにシグナルを送るようにしてください。例-アプリケーションが例外をスローし、メソッドが呼び出されて、他のものに完了したことを通知しない場合。これにより、他のスレッドが「ウェイクアップ」することはありません。

ケースを実装する前に、このパラダイムをよりよく理解するために、イベントとイベントハンドラーを使用する概念を検討することをお勧めします。

または、mutexを使用してブロッキング関数呼び出しを使用できます。これにより、リソースが解放されるまでスレッドが待機します。これを行うには、次のような適切なスレッド同期が必要です。

Thread-A Locks lock-a
Run thread-B
Thread-B waits for lock-a
Thread-A unlocks lock-a (causing Thread-B to continue)
Thread-A waits for lock-b 
Thread-B completes and unlocks lock-b
2
Klathzazt

迅速で汚れたものが必要な場合は、whileループ内にThread.sleep()呼び出しを追加するだけです。データベースライブラリが変更できないものである場合、他に簡単な解決策はありません。待機期間の準備ができるまでデータベースをポーリングしても、パフォーマンスは低下しません。

while (!dbthread.isReady()) {
  Thread.sleep(250);
}

エレガントなコードとは言えないものの、作業は完了します。

データベースコードを変更できる場合は、他の回答で提案されているようにミューテックスを使用することをお勧めします。

2
Mario Ortegón

あるスレッドのブロッキングキューから読み取り、別のスレッドで書き込むことができます。

2
Ingo

以来

  1. join()は除外されました
  2. CountDownLatch を既に使用している
  3. Future.get() は他の専門家によって既に提案されていますが、

あなたは他の選択肢を検討することができます:

  1. invokeAll from ExecutorService

    invokeAll(Collection<? extends Callable<T>> tasks)
    

    指定されたタスクを実行し、すべてが完了したときにステータスと結果を保持するFutureのリストを返します。

  2. ForkJoinPool または newWorkStealingPool from Executors(Java 8リリース以降)

    使用可能なすべてのプロセッサーをターゲット並列処理レベルとして使用して、ワークスチールスレッドプールを作成します。

1
Ravindra babu