2011-01-14 6 views
36

У меня есть поток, загружающий данные, и я хочу дождаться завершения загрузки до загрузки данных. Есть ли стандартный способ сделать это?Java Подождите, пока закончится поток

Дополнительная информация:

У меня есть класс загрузки, который получает данные из URL (Serialized Pojos). Загрузка является Runnable и Observable. Он отслеживает загруженные и загружаемые байты. У меня есть индикатор выполнения, который отображает прогресс для Пользователя. Графический интерфейс пользователя Download Download, чтобы обновить индикатор выполнения.

Когда POJO загружается, я хочу его получить и перейти к следующему шагу. Каждый шаг должен дождаться завершения предыдущего. Проблема в том, что я не могу придумать способ приостановить мое приложение, чтобы дождаться потока загрузки. После завершения загрузки я хочу вызвать download.getObject(), который вернет данные как объект. Затем я могу использовать его и перейти к следующей загрузке.

У меня есть вспомогательный класс, который управляет URL-адресами для загрузки и делает все вызовы Download. Этот вызов вызовет getObject и выполнит кастинг. Gui вызывает helper.getUser(). хелпер запускает поток, и я хочу, чтобы он «знал», когда он закончен, чтобы он мог вернуть литой объект.

Любые предложения/примеры? Я нахожусь на начальных этапах этого проекта, поэтому я готов его изменить.

Благодарим вас.

Обновление:

Я следовал http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get и использовал модальный блокировать, пока поток не закончен. Код был очень грязным, и мне не нравится этот подход. Я буду продолжать пытаться найти «чистый» способ обработать рабочий процесс процессов загрузки.

ответ

51

Thread есть способ, который делает это за вас. join будет блокироваться, пока поток не завершит выполнение.

+0

Я попытался присоединиться(), но сохранил GUI от обновления. Причина, по которой я использовал Thread, заключалась в том, чтобы сохранить обновление gui во время загрузки. Вызов соединения прекращает это. – Allan

+4

join() будет работать. если графический интерфейс перестает обновляться, вы, вероятно, должны быть более подробными о том, какие потоки вы присоединяете. Я бы не присоединился к EDT. – akf

+2

Затем вам нужно убедиться, что вы не вызываете 'join()' в качающемся потоке. Вы можете сделать это, создав один поток, который отвечает за ваш поток загрузки. Это, по сути, просто фоновой работник, о котором вы можете забыть. Это рабочий поток, который знает, когда загрузка заканчивается и что делать в этот момент. Вам просто нужно убедиться, что редактирование объектов качания выполняется в качающейся нити. – unholysampler

3

Как правило, если вы хотите дождаться окончания потока, вы должны позвонить на него join().

4

Я предполагаю, что вы вызываете свою загрузку в фоновом потоке, например, предоставляемом SwingWorker. Если это так, то просто вызовите следующий код последовательно в том же методе doInBackground того же SwingWorker.

10

SwingWorker имеет doInBackground(), который вы можете использовать для предварительной задачи. У вас есть возможность преформировать get() и дождаться завершения загрузки или вы можете переопределить метод done(), который будет предварительно сформирован в потоке отправки событий после завершения Swingworker.

У Swingworker есть преимущества для вашего нынешнего подхода, поскольку у него есть много функций, которые вы ищете, поэтому нет необходимости изобретать велосипед. Вы можете использовать методы getProgress() & setProgress() в качестве альтернативы наблюдателю на runnable для загрузки. Метод done(), как было указано выше, вызывается после того, как работник завершает выполнение и предварительно формируется на EDT, это позволяет загружать данные после завершения загрузки.

+2

+1 для 'SwingWorker'. – trashgod

1

Любые предложения/примеры? Я следовал SwingWorker ... Код был очень грязным, и мне не нравится этот подход.

Вместо get(), который ждет завершения, используйте process() и setProgress(), чтобы показать промежуточные результаты, как это было предложено в этом простом example или это связано example.

36

Вы можете использовать CountDownLatch от java.util.concurrent. Это очень полезно при ожидании завершения одного или нескольких потоков, прежде чем продолжить выполнение в ожидающем потоке.

Например, ожидая трех задач для завершения:

CountDownLatch latch = new CountDownLatch(3); 
... 
latch.await(); // Wait for countdown 

Другой поток (s), то каждый вызов latch.countDown(), когда в комплекте с их задачами. Как только обратный отсчет будет завершен, три в этом примере, выполнение будет продолжено.

+0

Тот же вопрос, что и @Alzoid. Пользовательский интерфейс перестает работать. –

+0

Работает идеально для приложений без GUI. – JRichardsz

0

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

1

Улучшенные альтернативы методу join() были разработаны в течение определенного периода времени.

ExecutorService.html#invokeAll является одной альтернативой.

Выполняет заданные задачи, возвращая список фьючерсов, содержащих их статус и результаты, когда все завершено. Future.isDone() истинно для каждого элемента возвращаемого списка.

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

ForkJoinPool или Executors.html#newWorkStealingPool предоставляет другие альтернативы для достижения той же цели.

Пример фрагмента кода:

import java.util.concurrent.*; 

import java.util.*; 

public class InvokeAllDemo{ 
    public InvokeAllDemo(){ 
     System.out.println("creating service"); 
     ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 

     List<MyCallable> futureList = new ArrayList<MyCallable>(); 
     for (int i=0; i<10; i++){ 
      MyCallable myCallable = new MyCallable((long)i); 
      futureList.add(myCallable); 
     } 
     System.out.println("Start"); 
     try{ 
      List<Future<Long>> futures = service.invokeAll(futureList); 
     }catch(Exception err){ 
      err.printStackTrace(); 
     } 
     System.out.println("Completed"); 
     service.shutdown(); 
    } 
    public static void main(String args[]){ 
     InvokeAllDemo demo = new InvokeAllDemo(); 
    } 
    class MyCallable implements Callable<Long>{ 
     Long id = 0L; 
     public MyCallable(Long val){ 
      this.id = val; 
     } 
     public Long call(){ 
      // Add your business logic 
      return id; 
     } 
    } 
} 
0

Вы можете использовать join() ждать, чтобы закончить все темы. Хранить все объекты потоков в глобальном массиве во время создания потоков. После этого держать его в петлю, как показано ниже:

for (int i = 0; i < 10; i++) 
{ 
    Thread T1 = new Thread(new ThreadTest(i));     
    T1.start();     
    arrThreads.add(T1); 
} 

for (int i = 0; i < arrThreads.size(); i++) 
{ 
    arrThreads.get(i).join(); 
} 

Проверьте здесь для подробностей: http://www.letmeknows.com/2017/04/24/wait-for-threads-to-finish-java

Смежные вопросы