StartDownload()
3つのスレッドを開始するという名前のメソッドを持つオブジェクトがあります。
各スレッドの実行が終了したときに通知を受け取るにはどうすればよいですか?
スレッドの1つ(またはすべて)が終了しているか、まだ実行中であるかを知る方法はありますか?
StartDownload()
3つのスレッドを開始するという名前のメソッドを持つオブジェクトがあります。
各スレッドの実行が終了したときに通知を受け取るにはどうすればよいですか?
スレッドの1つ(またはすべて)が終了しているか、まだ実行中であるかを知る方法はありますか?
回答:
これを行う方法はいくつかあります。
アイデア#5の実装方法は?1つの方法は、最初にインターフェースを作成することです。
public interface ThreadCompleteListener {
void notifyOfThreadComplete(final Thread thread);
}
次に、次のクラスを作成します。
public abstract class NotifyingThread extends Thread {
private final Set<ThreadCompleteListener> listeners
= new CopyOnWriteArraySet<ThreadCompleteListener>();
public final void addListener(final ThreadCompleteListener listener) {
listeners.add(listener);
}
public final void removeListener(final ThreadCompleteListener listener) {
listeners.remove(listener);
}
private final void notifyListeners() {
for (ThreadCompleteListener listener : listeners) {
listener.notifyOfThreadComplete(this);
}
}
@Override
public final void run() {
try {
doRun();
} finally {
notifyListeners();
}
}
public abstract void doRun();
}
その後、各スレッドが拡張NotifyingThread
され、実装run()
する代わりに実装されdoRun()
ます。したがって、完了すると、通知を待っている人に自動的に通知されます。
最後に、メインクラス(すべてのスレッド(または少なくとも通知を待機しているオブジェクト)を開始するクラス)でクラスを変更し、implement ThreadCompleteListener
各スレッドを作成した直後に自分自身をリスナーのリストに追加します。
NotifyingThread thread1 = new OneOfYourThreads();
thread1.addListener(this); // add ourselves as a listener
thread1.start(); // Start the Thread
次に、各スレッドが終了すると、notifyOfThreadComplete
完了した(またはクラッシュした)スレッドインスタンスを使用してメソッドが呼び出されます。
より良いがにされることに注意してくださいimplements Runnable
ではなく、extends Thread
ためにNotifyingThread
拡張スレッドは通常、新しいコードでは推奨されて。しかし、私はあなたの質問にコーディングしています。NotifyingThread
クラスを変更して実装Runnable
する場合は、スレッドを管理するコードの一部を変更する必要があります。これは非常に簡単です。
notify
、run
メソッドではなくメソッドの後にメソッドを呼び出すことができるかどうか尋ねていました。
CyclicBarrierを使用したソリューション
public class Downloader {
private CyclicBarrier barrier;
private final static int NUMBER_OF_DOWNLOADING_THREADS;
private DownloadingThread extends Thread {
private final String url;
public DownloadingThread(String url) {
super();
this.url = url;
}
@Override
public void run() {
barrier.await(); // label1
download(url);
barrier.await(); // label2
}
}
public void startDownload() {
// plus one for the main thread of execution
barrier = new CyclicBarrier(NUMBER_OF_DOWNLOADING_THREADS + 1); // label0
for (int i = 0; i < NUMBER_OF_DOWNLOADING_THREADS; i++) {
new DownloadingThread("http://www.flickr.com/someUser/pic" + i + ".jpg").start();
}
barrier.await(); // label3
displayMessage("Please wait...");
barrier.await(); // label4
displayMessage("Finished");
}
}
label0-実行中のスレッド数+実行のメインスレッド(startDownload()が実行されている)の1に等しい数のパーティで循環バリアが作成されます
ラベル1 -n番目のDownloadingThreadが待合室に入る
ラベル3 -NUMBER_OF_DOWNLOADING_THREADSが待合室に入りました。実行のメインスレッドはそれらを解放して、ほぼ同時にダウンロードジョブの実行を開始します
ラベル4-実行のメインスレッドが待合室に入ります。これは、理解する必要があるコードの「トリッキー」な部分です。どのスレッドが2回目に待合室に入るかは関係ありません。部屋に最後に入るスレッドがどれであっても、他のすべてのダウンロードスレッドがダウンロードジョブを確実に終了することが重要です。
ラベル2 -n番目のDownloadingThreadはダウンロードジョブを完了し、待合室に入ります。それが最後のスレッドである場合、つまり実行のメインスレッドを含め、すでにNUMBER_OF_DOWNLOADING_THREADSが入力されている場合、メインスレッドは、他のすべてのスレッドのダウンロードが終了したときにのみ実行を継続します。
あなたは本当にを使用するソリューションを好むはずjava.util.concurrent
です。トピックに関するJosh BlochやBrian Goetzを見つけて読んでください。
java.util.concurrent.*
スレッドを使用しておらず、直接スレッドを使用する責任join()
がある場合は、スレッドがいつ完了したかを知るために使用する必要があります。これが超シンプルなコールバックメカニズムです。まず、Runnable
インターフェースを拡張してコールバックを作成します。
public interface CallbackRunnable extends Runnable {
public void callback();
}
次に、ランナブルを実行するエグゼキューターを作成し、完了したらコールバックします。
public class CallbackExecutor implements Executor {
@Override
public void execute(final Runnable r) {
final Thread runner = new Thread(r);
runner.start();
if ( r instanceof CallbackRunnable ) {
// create a thread to perform the callback
Thread callerbacker = new Thread(new Runnable() {
@Override
public void run() {
try {
// block until the running thread is done
runner.join();
((CallbackRunnable)r).callback();
}
catch ( InterruptedException e ) {
// someone doesn't want us running. ok, maybe we give up.
}
}
});
callerbacker.start();
}
}
}
CallbackRunnable
インターフェイスに追加するもう1つの種類の明らかなことは、例外を処理する手段であるため、おそらくpublic void uncaughtException(Throwable e);
そこに行を入れ、エグゼキューターにThread.UncaughtExceptionHandlerをインストールして、そのインターフェイスメソッドに送信します。
しかし、すべてのことを行うと、本当に匂いがし始めjava.util.concurrent.Callable
ます。java.util.concurrent
プロジェクトで許可されている場合は、実際に使用する必要があります。
runner.join()
スレッドが終了したことがわかっているので、このコールバックメカニズムから何が得られるのか、単純に呼び出してから、その後に必要なコードを取得することについては、少し不明確です。そのコードをランナブルのプロパティとして定義するだけで、ランナブルごとに異なるものを持つことができますか?
runner.join()
待つ最も直接的な方法です。OPはメインの呼び出しスレッドをブロックしたくないと思っていました。ダウンロードごとに「通知」を求められたためです。これにより、非同期で通知を受ける1つの方法が提供されました。
それらが完了するのを待ちますか?その場合は、Joinメソッドを使用します。
確認したいだけの場合はisAliveプロパティもあります。
Thread
toを伝えstart
ますが、呼び出しはすぐに戻ります。isAlive
簡単なフラグテストである必要がありますが、Googleでググるとその方法はでしたnative
。
次のいずれかの値を持つThread.State列挙のインスタンスを返すgetState()でスレッドインスタンスに問い合わせることができます。
* NEW
A thread that has not yet started is in this state.
* RUNNABLE
A thread executing in the Java virtual machine is in this state.
* BLOCKED
A thread that is blocked waiting for a monitor lock is in this state.
* WAITING
A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
* TIMED_WAITING
A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
* TERMINATED
A thread that has exited is in this state.
ただし、3つの子が完了するのを待つマスタースレッドを使用する方が良い設計になると思います。マスターは、他の3つが完了すると実行を継続します。
Executors
オブジェクトを使用して、ExecutorServiceスレッドプールを作成する こともできます。次に、このinvokeAll
メソッドを使用して各スレッドを実行し、Futureを取得します。これは、すべての実行が完了するまでブロックされます。他のオプションは、プールを使用してそれぞれを実行してawaitTermination
から、プールの実行が完了するまでブロックを呼び出すことです。shutdown
タスクの追加が完了したら、必ず()を呼び出してください。
過去6年間にマルチスレッドの面で多くのことが変更されました。
join()
API を使用してロックする代わりに、
1. ExecutorService invokeAll()
API
指定されたタスクを実行し、すべてが完了すると、ステータスと結果を保持するFutureのリストを返します。
1つ以上のスレッドが、他のスレッドで実行されている一連の操作が完了するまで待機することを可能にする同期支援。
A
CountDownLatch
は指定されたカウントで初期化されます。awaitメソッドは、countDown()
メソッドの呼び出しにより現在のカウントがゼロに達するまでブロックします。その後、待機中のスレッドがすべて解放され、その後のawaitの呼び出しはすぐに戻ります。これはワンショット現象です。カウントをリセットできません。カウントをリセットするバージョンが必要な場合は、CyclicBarrierの使用を検討してください。
3. ForkJoinPoolか newWorkStealingPool()
にエグゼキュータは、他の方法です。
4.送信からすべてのFuture
タスクを繰り返し、オブジェクトのExecutorService
ブロック呼び出しget()
でステータスを確認しFuture
ます
関連するSEの質問をご覧ください。
スレッド クラスのjavadocを確認することをお勧めします。
スレッド操作には複数のメカニズムがあります。
メインスレッドjoin()
は3つのスレッドを順番に実行でき、3つすべてが完了するまで続行されません。
生成されたスレッドのスレッド状態を一定間隔でポーリングします。
生成されたスレッドをすべて別のスレッドに入れ、ThreadGroup
をポーリングしactiveCount()
て、ThreadGroup
それが0になるまで待ちます。
スレッド間通信用のカスタムコールバックまたはリスナータイプのインターフェイスをセットアップします。
他にもまだ足りない方法がたくさんあると思います。
シンプルで短く、理解しやすく、完璧に機能するソリューションを次に示します。別のスレッドが終了したときに画面に描画する必要がありました。メインスレッドが画面を制御しているため、できませんでした。そう:
(1)グローバル変数を作成しましたboolean end1 = false;
。スレッドは終了時にtrueに設定します。これは、「postDelayed」ループによってメインスレッドでピックアップされ、そこで応答されます。
(2)私のスレッドには以下が含まれます:
void myThread() {
end1 = false;
new CountDownTimer(((60000, 1000) { // milliseconds for onFinish, onTick
public void onFinish()
{
// do stuff here once at end of time.
end1 = true; // signal that the thread has ended.
}
public void onTick(long millisUntilFinished)
{
// do stuff here repeatedly.
}
}.start();
}
(3)幸い、「postDelayed」はメインスレッドで実行されるため、1秒に1回、他のスレッドをチェックします。他のスレッドが終了すると、次に何をしたいかを開始できます。
Handler h1 = new Handler();
private void checkThread() {
h1.postDelayed(new Runnable() {
public void run() {
if (end1)
// resond to the second thread ending here.
else
h1.postDelayed(this, 1000);
}
}, 1000);
}
(4)最後に、次のコードを呼び出して、コードのどこかですべてを実行します。
void startThread()
{
myThread();
checkThread();
}
ThreadPoolExecutor
クラスを使うのが一番簡単だと思います。
フックメソッド
このクラスは、各タスクの実行前と実行後に呼び出される
beforeExecute(java.lang.Thread, java.lang.Runnable)
、保護されたオーバーライド可能なafterExecute(java.lang.Runnable, java.lang.Throwable)
メソッドとメソッドを提供します。これらは、実行環境を操作するために使用できます。たとえば、ThreadLocalsの再初期化、統計の収集、ログエントリの追加などです。さらに、メソッドterminated()
をオーバーライドして、Executorが完全に終了した後に実行する必要がある特別な処理を実行できます。
これがまさに私たちが必要としていることです。私たちは、上書きされます afterExecute()
各スレッドが実行された後にコールバックを取得すると無効になりますterminated()
すべてのスレッドが実行されたときに知っています。
だからここにあなたがすべきことです
エグゼキューターを作成します。
private ThreadPoolExecutor executor;
private int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private void initExecutor() {
executor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, //core pool size
NUMBER_OF_CORES * 2, //max pool size
60L, //keep aive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
//Yet another thread is finished:
informUiAboutProgress(executor.getCompletedTaskCount(), listOfUrisToProcess.size());
}
}
};
@Override
protected void terminated() {
super.terminated();
informUiThatWeAreDone();
}
}
スレッドを開始します。
private void startTheWork(){
for (Uri uri : listOfUrisToProcess) {
executor.execute(new Runnable() {
@Override
public void run() {
doSomeHeavyWork(uri);
}
});
}
executor.shutdown(); //call it when you won't add jobs anymore
}
InsideメソッドinformUiThatWeAreDone();
は、たとえばUIの更新など、すべてのスレッドが完了したときに必要なことをすべて実行します。
注:synchronized
並行して作業を行うため、メソッドの使用を忘れないでください。synchronized
別のsynchronized
メソッドからメソッドを呼び出す場合は、十分に注意してください。これはしばしばデッドロックにつながります
お役に立てれば!
プロパティ変更のサポートが組み込まれているSwingWorkerを使用することもできます。状態変更リスナーの例については、addPropertyChangeListener()またはget()メソッドを参照してください。