11 ответов:
есть несколько способов сделать это:
- использовать нить.присоединяйтесь() в вашем основном потоке ждать в блокирующем режиме для каждого потока, чтобы завершить, или
- Регистрация нить.isAlive () в режиме опроса - обычно не рекомендуется-ждать, пока каждый поток не будет завершен, или
- неортодоксально, для каждого потока в вопросе, вызов setUncaughtExceptionHandler для вызова метода объекта, и запрограммируйте каждый поток, чтобы бросить неперехваченное исключение, когда оно завершится, или
- использовать блокировки или синхронизаторы или механизмы от java.утиль.одновременно или
- более ортодоксально, создайте слушателя в своем основном потоке, а затем запрограммируйте каждый из своих потоков, чтобы сообщить слушателю, что они завершены.
как реализовать идею #5? Ну, один из способов-сначала создать интерфейс:
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затем вы должны изменить некоторые из вашего кода, который управляет потоками, что довольно просто делать.
решение с помощью 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 - n-й DownloadingThread входит в зал ожидания
ярлык 3 - NUMBER_OF_DOWNLOADING_THREADS вошли в зал ожидания. Главный поток выполнения освобождает их, чтобы начать выполнять свои задания загрузки в более или менее то же время
ярлык 4 - основной поток выполнения переходит в зал ожидания. Это самая "хитрая" часть кода, чтобы понять. Не имеет значения, какая нить войдет в зал ожидания во второй раз. Важно, чтобы любой поток, входящий в комнату последним, гарантировал, что все другие потоки загрузки закончили их загрузку джобс.
ярлык 2 - n-й DownloadingThread закончил свою работу загрузки и входит в зал ожидания. Если он последний, т. е. уже number_of_downloading_threads ввели его, включая основной поток выполнения, основной поток продолжит свое выполнение только тогда, когда все остальные потоки закончат загрузку.
вы должны действительно предпочел решение, которое использует
java.util.concurrent. Найдите и прочитайте Джоша Блоха и / или Брайана Гетца по этой теме.если вы не используете
java.util.concurrent.*и берут на себя ответственность за использование потоков напрямую, то вы должны, вероятно, использоватьjoin()чтобы знать, когда поток выполняется. Вот супер простой механизм обратного вызова. Сначала расширьтеRunnableинтерфейс для обратного вызова:public interface CallbackRunnable extends Runnable { public void callback(); }затем сделайте исполнителя, который будет выполнять ваши runnable и перезвонить вам, когда это будет сделано.
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интерфейс является средством для обработки любых исключений, так что, возможно, поставитьpublic void uncaughtException(Throwable e);строка там и в вашем исполнителе, установите поток.UncaughtExceptionHandler, чтобы отправить вас в этот метод интерфейса.но делать все это действительно начинает пахнуть
java.util.concurrent.Callable. Вы должны действительно смотреть на использованиеjava.util.concurrentЕсли ваш проект позволяет это.
вы хотите подождать, пока они закончат? Если это так, используйте метод Join.
существует также свойство isAlive, если вы просто хотите его проверить.
вы можете опросить экземпляр потока с помощью 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 закончат.
Я бы предложил посмотреть на javadoc для Thread класса.
у вас есть несколько механизмов для обработки потоков.
ваш основной поток может
join()три потока последовательно, а затем не будет продолжаться, пока все три не будут сделаны.опрос состояния потока порожденных потоков с интервалами.
поставить все порожденные потоки в отдельный
ThreadGroupи опрос элементactiveCount()наThreadGroupи ждать его, чтобы добраться до 0.настройка пользовательского типа интерфейса обратного вызова или прослушивателя для межпотоковой связи.
Я уверен, что есть много других способов, которые я все еще не хватает.
вы также можете использовать
Executorsобъект для создания ExecutorService пул потоков. Тогда используйтеinvokeAllметод для запуска каждого из ваших потоков и получения фьючерсов. Это будет блокировать, пока все не завершатся. Другой вариант-выполнить каждый из них с помощью пула, а затем вызватьawaitTerminationблокировать до тех пор, пока бассейн не завершит выполнение. Просто будьте уверены, чтобы позвонитьshutdown() когда вы закончите добавление задач.
многие вещи были изменены за последние 6 лет на многопоточном фронте.
вместо
join()и блокировка API, вы можете использовать1.ExecutorService
invokeAll()APIвыполняет заданные задачи, возвращая список фьючерсов с их статусом и результатами, когда все завершится.
помощь синхронизации, которая позволяет один или несколько потоки, чтобы дождаться завершения набора операций, выполняемых в других потоках.
A
CountDownLatchинициализируется с заданным количеством. Методы await блокируются до тех пор, пока текущее количество не достигнет нуля из-за вызововcountDown()метод, после которого все ожидающие потоки освобождаются и любые последующие вызовы await возвращаются немедленно. Это одноразовое явление-счетчик не может быть сброшен. Если вам нужна версия, которая сбрасывает счетчик, рассмотрите возможность использования CyclicBarrier.3.ForkJoinPool или
newWorkStealingPool()на исполнители другой способ4.Перебрать все
Futureзадачи от отправки наExecutorServiceи проверить состояние с блокировкой вызоваget()onFutureобъектпосмотрите на связанные вопросы SE:
как ждать поток, который порождает его собственный поток?
исполнители: как синхронно ждать, пока все задачи завершены, если задачи создаются рекурсивно?
вот решение, которое является простым, коротким, легко понять, и прекрасно работает для меня. Мне нужно было рисовать на экране, когда заканчивается другой поток; но не мог, потому что основной поток имеет контроль над экраном. Итак:
(1) я создал глобальную переменную:
boolean end1 = false;поток устанавливает его в true при завершении. Это подхватывается в mainthread циклом "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" работает в основном потоке, так что это где проверить другой поток один раз в секунду. Когда другой поток заканчивается, это может начать все, что мы хотим сделать дальше.
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(); }
вы также можете использовать SwingWorker, который имеет встроенную поддержку изменения свойств. Смотрите addPropertyChangeListener() или get () метод на примере изменения состояния слушателя.
посмотрите на документацию Java для класса Thread. Вы можете проверить состояние потока. Если поместить три потока в переменные-члены, то все три потока могут считывать состояния друг друга.
вы должны быть немного осторожны, хотя, потому что вы можете вызвать состояние гонки между потоками. Просто старайтесь избегать сложной логики, основанной на состоянии других потоков. Определенно избегайте записи нескольких потоков в одни и те же переменные.
Comments