Java дождаться завершения потока

Java Ожидание завершения потока

У меня есть поток, загружающий данные, и я хочу подождать, пока загрузка не будет закончена, прежде чем загружать данные. Есть ли стандартный способ сделать это? Дополнительная информация: У меня есть класс Download, который получает данные из URL-адреса (Serialized POJO). Загрузка является Runnable и Observable. Он отслеживает загруженные и загружаемые байты. У меня есть индикатор выполнения, который отображает прогресс для Пользователя. GUI наблюдает Download для обновления индикатора выполнения. Когда POJO загружается, я хочу его получить и перейти к следующему шагу. Каждый шаг должен дождаться завершения предыдущего. Проблема в том, что я не могу придумать способ приостановить мое приложение, чтобы дождаться потока загрузки. После завершения загрузки я хочу вызвать download.getObject(), который вернет данные как объект. Затем я могу использовать его и продолжить следующую загрузку. У меня есть вспомогательный класс, который управляет URL-адресами для загрузки и делает все вызовы Download. Этот вызов вызовет getObject и выполнит кастинг. Gui вызывает helper.getUser(). хелпер запускает поток, и я хочу, чтобы он «знал», когда он закончен, чтобы он мог вернуть литой объект. Любые предложения/примеры? Я нахожусь на начальных этапах этого проекта, поэтому я готов его изменить. Благодарим вас. Update: Я следил за http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get и использовал модальный блок, пока поток не завершится. Код был очень грязным, и мне не нравится этот подход. Я буду продолжать пытаться найти «чистый» способ обработать рабочий процесс процессов загрузки.

Читайте также:  Компоненты vue в html

Источник

ExecutorService — Ожидание завершения потоков

ФреймворкExecutorService упрощает обработку задач в нескольких потоках. Мы собираемся проиллюстрировать некоторые сценарии, в которых мы ждем, пока потоки завершат свое выполнение.

Кроме того, мы покажем, как корректно завершить работуExecutorService и дождаться, пока уже запущенные потоки завершат свое выполнение.

2. После выключенияExecutor’s

При использованииExecutor, мы можем выключить его, вызвав методыshutdown() илиshutdownNow(). Although, it won’t wait until all threads stop executing.

Ожидание завершения выполнения существующих потоков может быть достигнуто с помощью методаawaitTermination().

Это блокирует поток до тех пор, пока все задачи не завершат свое выполнение или не истечет указанное время ожидания:

public void awaitTerminationAfterShutdown(ExecutorService threadPool) < threadPool.shutdown(); try < if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) < threadPool.shutdownNow(); >> catch (InterruptedException ex) < threadPool.shutdownNow(); Thread.currentThread().interrupt(); >>

3. ИспользуяCountDownLatch

Затем давайте рассмотрим другой подход к решению этой проблемы — использованиеCountDownLatch для сигнализации о завершении задачи.

Мы можем инициализировать его значением, которое представляет, сколько раз он может быть уменьшен до того, как будут уведомлены все потоки, вызвавшие методawait().

Например, если нам нужно, чтобы текущий поток ожидал, пока другой потокN завершит свое выполнение, мы можем инициализировать защелку, используяN:

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(10); CountDownLatch latch = new CountDownLatch(2); for (int i = 0; i < 2; i++) < WORKER_THREAD_POOL.submit(() -> < try < // . latch.countDown(); >catch (InterruptedException e) < Thread.currentThread().interrupt(); >>); > // wait for the latch to be decremented by the two remaining threads latch.await();

4. ИспользуяinvokeAll()

Первый подход, который мы можем использовать для запуска потоков, — это методinvokeAll(). The method returns a list of Future objects after all tasks finish or the timeout expires.

Также мы должны отметить, что порядок возвращаемых объектовFuture такой же, как и список предоставленных объектовCallable:

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(10); List> callables = Arrays.asList( new DelayedCallable("fast thread", 100), new DelayedCallable("slow thread", 3000)); long startProcessingTime = System.currentTimeMillis(); List> futures = WORKER_THREAD_POOL.invokeAll(callables); awaitTerminationAfterShutdown(WORKER_THREAD_POOL); long totalProcessingTime = System.currentTimeMillis() - startProcessingTime; assertTrue(totalProcessingTime >= 3000); String firstThreadResponse = futures.get(0).get(); assertTrue("fast thread".equals(firstThreadResponse)); String secondThreadResponse = futures.get(1).get(); assertTrue("slow thread".equals(secondThreadResponse));

5. ИспользуяExecutorCompletionService

Другой подход к запуску нескольких потоков — использованиеExecutorCompletionService.. Он использует предоставленныйExecutorService для выполнения задач.

Одно отличие отinvokeAll() — это порядок, в котором возвращаютсяFutures,, представляющие выполненные задачи. ExecutorCompletionService uses a queue to store the results in the order they are finished, аinvokeAll() возвращает список, имеющий тот же последовательный порядок, что и созданный итератором для данного списка задач:

CompletionService service = new ExecutorCompletionService<>(WORKER_THREAD_POOL); List callables = Arrays.asList( new DelayedCallable("fast thread", 100), new DelayedCallable("slow thread", 3000)); for (Callable callable : callables)

Доступ к результатам можно получить с помощью методаtake():

long startProcessingTime = System.currentTimeMillis(); Future future = service.take(); String firstThreadResponse = future.get(); long totalProcessingTime = System.currentTimeMillis() - startProcessingTime; assertTrue("First response should be from the fast thread", "fast thread".equals(firstThreadResponse)); assertTrue(totalProcessingTime >= 100 && totalProcessingTime < 1000); LOG.debug("Thread finished after: " + totalProcessingTime + " milliseconds"); future = service.take(); String secondThreadResponse = future.get(); totalProcessingTime = System.currentTimeMillis() - startProcessingTime; assertTrue( "Last response should be from the slow thread", "slow thread".equals(secondThreadResponse)); assertTrue( totalProcessingTime >= 3000 && totalProcessingTime < 4000); LOG.debug("Thread finished after: " + totalProcessingTime + " milliseconds"); awaitTerminationAfterShutdown(WORKER_THREAD_POOL);

6. Заключение

В зависимости от варианта использования у нас есть различные варианты ожидания завершения потоков.

CountDownLatch полезен, когда нам нужен механизм для уведомления одного или нескольких потоков о завершении набора операций, выполняемых другими потоками.

ExecutorCompletionService полезен, когда нам нужно получить доступ к результату задачи как можно скорее, и другие подходы, когда мы хотим дождаться завершения всех запущенных задач.

Исходный код статьи доступенover on GitHub.

Источник

Java дождаться завершения потока

Для создания нового потока мы можем создать новый класс, либо наследуя его от класса Thread, либо реализуя в классе интерфейс Runnable .

Наследование от класса Thread

Создадим свой класс на основе Thread:

class JThread extends Thread < JThread(String name)< super(name); >public void run() < System.out.printf("%s started. \n", Thread.currentThread().getName()); try< Thread.sleep(500); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.printf("%s fiished. \n", Thread.currentThread().getName()); > > public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); new JThread("JThread").start(); System.out.println("Main thread finished. "); >>

Класс потока называется JThread. Предполагается, что в конструктор класса передается имя потока, которое затем передается в конструктор базового класса. В конструктор своего класса потока мы можем передать различные данные, но главное, чтобы в нем вызывался конструктор базового класса Thread, в который передается имя потока.

И также в JThread переопределяется метод run() , код которого собственно и будет представлять весь тот код, который выполняется в потоке.

В методе main для запуска потока JThread у него вызывается метод start() , после чего начинается выполнение того кода, который определен в методе run:

Main thread started. Main thread finished. JThread started. JThread finished.

Здесь в методе main в конструктор JThread передается произвольное название потока, и затем вызывается метод start() . По сути этот метод как раз и вызывает переопределенный метод run() класса JThread.

Обратите внимание, что главный поток завершает работу раньше, чем порожденный им дочерний поток JThread.

Аналогично созданию одного потока мы можем запускать сразу несколько потоков:

public static void main(String[] args)

Main thread started. Main thread finished. JThread 2 started. JThread 5 started. JThread 4 started. JThread 1 started. JThread 3 started. JThread 1 finished. JThread 2 finished. JThread 5 finished. JThread 4 finished. JThread 3 finished.

Ожидание завершения потока

При запуске потоков в примерах выше Main thread завершался до дочернего потока. Как правило, более распространенной ситуацией является случай, когда Main thread завершается самым последним. Для этого надо применить метод join() . В этом случае текущий поток будет ожидать завершения потока, для которого вызван метод join:

public static void main(String[] args) < System.out.println("Main thread started. "); JThread t= new JThread("JThread "); t.start(); try< t.join(); >catch(InterruptedException e) < System.out.printf("%s has been interrupted", t.getName()); >System.out.println("Main thread finished. "); >

Метод join() заставляет вызвавший поток (в данном случае Main thread) ожидать завершения вызываемого потока, для которого и применяется метод join (в данном случае JThread).

Main thread started. JThread started. JThread finished. Main thread finished.

Если в программе используется несколько дочерних потоков, и надо, чтобы Main thread завершался после дочерних, то для каждого дочернего потока надо вызвать метод join.

Реализация интерфейса Runnable

Другой способ определения потока представляет реализация интерфейса Runnable . Этот интерфейс имеет один метод run :

В методе run() собственно определяется весь тот код, который выполняется при запуске потока.

После определения объекта Runnable он передается в один из конструкторов класса Thread:

Thread(Runnable runnable, String threadName)

Для реализации интерфейса определим следующий класс MyThread:

class MyThread implements Runnable < public void run()< System.out.printf("%s started. \n", Thread.currentThread().getName()); try< Thread.sleep(500); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.printf("%s finished. \n", Thread.currentThread().getName()); > > public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); Thread myThread = new Thread(new MyThread(),"MyThread"); myThread.start(); System.out.println("Main thread finished. "); >>

Реализация интерфейса Runnable во многом аналогична переопределению класса Thread. Также в методе run определяется простейший код, который усыпляет поток на 500 миллисекунд.

В методе main вызывается конструктор Thread, в который передается объект MyThread. И чтобы запустить поток, вызывается метод start() . В итоге консоль выведет что-то наподобие следующего:

Main thread started. Main thread finished. MyThread started. MyThread finished.

Поскольку Runnable фактически представляет функциональный интерфейс, который определяет один метод, то объект этого интерфейса мы можем представить в виде лямбда-выражения:

public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); Runnable r = ()-> < System.out.printf("%s started. \n", Thread.currentThread().getName()); try< Thread.sleep(500); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.printf("%s finished. \n", Thread.currentThread().getName()); >; Thread myThread = new Thread(r,"MyThread"); myThread.start(); System.out.println("Main thread finished. "); > >

Источник

Оцените статью