Ответ 1
Thread
имеет метод, который делает это для вас присоединиться, который будет блокировать, пока поток не завершит выполнение.
У меня есть поток, загружающий данные, и я хочу подождать, пока загрузка не будет закончена, прежде чем загружать данные. Есть ли стандартный способ сделать это?
Дополнительная информация:
У меня есть класс 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 и использовал модальный блок, пока поток не завершится. Код был очень грязным, и мне не нравится этот подход. Я буду продолжать пытаться найти "чистый" способ обработать рабочий процесс процессов загрузки.
Thread
имеет метод, который делает это для вас присоединиться, который будет блокировать, пока поток не завершит выполнение.
Вы можете использовать CountDownLatch
из пакета java.util.concurrent
. Это очень полезно при ожидании завершения одного или нескольких потоков, прежде чем продолжить выполнение в ожидающем потоке.
Например, ожидание завершения трех задач:
CountDownLatch latch = new CountDownLatch(3);
...
latch.await(); // Wait for countdown
Другой поток (ы), затем каждый вызов latch.countDown()
, когда они завершены с их задачами. Как только обратный отсчет будет завершен, три в этом примере, выполнение будет продолжено.
SwingWorker имеет doInBackground()
который вы можете использовать для выполнения задачи. У вас есть возможность вызвать get()
и дождаться завершения загрузки, или вы можете переопределить метод done()
который будет вызываться в потоке диспетчеризации событий после завершения SwingWorker.
Swingworker имеет преимущества перед вашим текущим подходом в том, что он обладает многими функциями, которые вы ищете, поэтому нет необходимости заново изобретать колесо. Вы можете использовать getProgress()
и setProgress()
в качестве альтернативы наблюдателю в исполняемом файле для прогресса загрузки. Метод done()
как я уже говорил выше, вызывается после того, как рабочий завершает выполнение, и выполняется в EDT, это позволяет загружать данные после завершения загрузки.
Лучшие альтернативы методу join() были разработаны в течение определенного периода времени.
ExecutorService.html # invokeAll является одной из альтернатив.
Выполняет заданные задачи, возвращая список Фьючерсов с их статусом и результатами, когда все выполнено. Future.isDone() имеет значение true для каждого элемента возвращаемого списка.
Обратите внимание, что завершенная задача могла быть завершена либо нормально, либо с помощью исключения. Результаты этого метода не определены, если данная коллекция была изменена во время выполнения этой операции.
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;
}
}
}
Я предполагаю, что вы вызываете свою загрузку в фоновом потоке, например, предоставляемом SwingWorker. Если это так, то просто вызовите следующий код последовательно в том же методе DoInBackground SwingWorker.
Как правило, если вы хотите дождаться окончания потока, вы должны называть join() на нем.
Вы можете использовать join()
чтобы дождаться завершения всех потоков. Сохраняйте все объекты потоков в глобальном ArrayList во время создания потоков. После этого держите его в цикле, как показано ниже:
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
Любые предложения/примеры? Я последовал за
SwingWorker
... Код был очень грязным, и мне не нравится этот подход.
Вместо get()
, который ждет завершения, используйте process()
и setProgress()
, чтобы показать промежуточные результаты, как это предлагается в этом простом примере или в этом пример.
Метод join() позволяет одному потоку ждать завершения другого. Однако, как и сон, соединение зависит от ОС для синхронизации, поэтому вы не должны предполагать, что соединение будет ждать столько, сколько вы укажете.