Java等待线程完成
我有一个线程下载数据,我想等到下载完成后才加载数据。有没有这样做的标准方式?Java等待线程完成
更多信息:
我有一个下载类,它从URL(序列化的POJO)获取数据。下载是Runnable和Observable。它跟踪下载的字节和下载大小。我有一个进度条,向用户显示进度。 GUI观察下载以更新进度条。
当POJO下载时,我想获取它并转到下一步。每一步都要等待前一个结束。问题是我不能想办法暂停我的应用程序来等待下载线程。一旦下载完成,我想调用download.getObject(),它将把数据作为一个对象返回。然后我可以投下它并继续下一次下载。
我有一个帮助类,管理下载的URL并使所有的下载调用。这个调用将调用getObject并执行转换。 Gui调用helper.getUser()。助手启动线程运行,并且我希望它在完成时“知道”,以便它可以返回铸造的对象。
任何建议/示例?我处于这个设计的开始阶段,所以我愿意改变它。
谢谢。
更新:
我跟着http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get和使用模式来阻止,直到线程完成。代码非常混乱,我不喜欢这种方法。我将继续努力寻找一种“干净”的方式来处理下载过程的工作流程。
我想象你在后台线程中调用你的下载,例如由SwingWorker提供。如果是这样,那么只需在同一个SwingWorker的doInBackground方法中依次调用您的下一个代码即可。
SwingWorker有doInBackground()
您可以使用它来执行任务。您可以选择预制get()
并等待下载完成,或者您可以覆盖done()
方法,一旦Swingworker完成,该方法将在事件分派线程上执行。
Swingworker具有您当前的方法的优点,因为它具有许多您正在寻找的功能,所以不需要重新发明*。您可以使用getProgress()
& setProgress()
方法作为可运行下载进度的观察者的替代方法。如上所述,done()
方法在worker完成执行后调用,并在EDT上执行,这允许您在下载完成后加载数据。
任何建议/示例?我跟着
SwingWorker
...代码非常混乱,我不喜欢这种方法。
代替get()
,它等待完成,使用process()
和setProgress()
显示中间结果,如以下简单example或该相关example建议。
您可以使用java.util.concurrent
包中的CountDownLatch
。当在等待线程中继续执行之前,等待一个或多个线程完成时非常有用。
例如,等了三个任务来完成:
CountDownLatch latch = new CountDownLatch(3);
...
latch.await(); // Wait for countdown
另一个线程(或多个),然后每次调用latch.countDown()
当与他们的任务完成了。一旦倒数完成,在这个例子中三个,执行将继续。
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;
}
}
}
您可以使用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
我想加盟(),但保留了GUI的更新。我使用线程的原因是在下载过程中保持GUI更新。调用加入停止发生。 – Allan 2011-01-14 13:41:19
join()将起作用。如果GUI停止更新,则可能必须更详细地说明要加入的线程。我不会加入美国东部时间。 – akf 2011-01-14 13:49:59
然后你需要确保你没有在swing线程中调用`join()`。你可以通过创建一个负责你的下载线程的线程来做到这一点。这基本上只是一个你可以忘记的背景工作者。工作线程知道何时下载完成以及该做什么。你只需要确保摆动对象的编辑是在摆动线程中完成的。 – unholysampler 2011-01-14 13:50:36