【问题标题】:Java Wait for thread to finishJava 等待线程完成
【发布时间】:2011-06-09 03:47:06
【问题描述】:

我有一个线程正在下载数据,我想等到下载完成后再加载数据。有这样做的标准方法吗?

更多信息:

我有一个从 URL(序列化 POJO)获取数据的下载类。下载是可运行和可观察的。它跟踪下载的字节数和下载大小。我有一个向用户显示进度的进度条。 GUI 观察下载以更新进度条。

下载 POJO 后,我想获取它并转到下一步。每一步都必须等待上一步完成。问题是我想不出一种方法来暂停我的应用程序以等待下载线程。下载完成后,我想调用download.getObject(),它将数据作为对象返回。然后我可以投射它并继续下一次下载。

我有一个帮助类来管理下载的 URL 并进行所有对下载的调用。此调用将调用 getObject 并进行强制转换。 Gui 打电话给helper.getUser()。 helper 启动线程运行,我希望它“知道”它何时完成,以便它可以返回强制转换的对象。

有什么建议/例子吗?我正处于这个设计的初始阶段,所以我愿意改变它。

更新:

我跟随http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get 并使用模态阻塞直到线程完成。代码非常混乱,我不喜欢这种方法。我将继续努力寻找一种“干净”的方式来处理下载过程的工作流程。

【问题讨论】:

    标签: java multithreading swing download wait


    【解决方案1】:

    Thread 有一个方法可以为您执行此操作join,它将阻塞直到线程完成执行。

    【讨论】:

    • 我尝试了 join() ,但这使 GUI 无法更新。我使用线程的原因是在下载时保持 gui 更新。调用 join 会阻止这种情况发生。
    • join() 将起作用。如果 GUI 停止更新,您可能需要更详细地了解要加入的线程。我不会加入 EDT。
    • 那么你需要确保你没有在swing线程中调用join()。您可以通过创建一个负责您的下载线程的线程来做到这一点。这本质上只是一个你可以忘记的后台工作人员。工作线程知道下载何时完成以及此时该做什么。您只需要确保在摆动线程中完成对摆动对象的编辑。
    • join() 不会重新抛出线程内抛出的异常。所以join()释放当前线程后,你不知道线程是否成功完成。
    【解决方案2】:

    您可以使用java.util.concurrent 包中的CountDownLatch。在等待一个或多个线程完成后再在等待线程中继续执行时非常有用。

    例如等待三个任务完成:

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

    当完成他们的任务时,其他线程然后每个调用latch.countDown()。一旦倒计时完成,在这个例子中是三个,执行将继续。

    【讨论】:

    • 非常适合没有 GUI 的应用程序。
    【解决方案3】:

    您可以使用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

    【讨论】:

      【解决方案4】:

      join() 方法的更好替代方案已经发展了一段时间。

      ExecutorService.html#invokeAll 是一种选择。

      执行给定的任务,返回一个 Futures 列表,在所有完成后保存它们的状态和结果。 Future.isDone() 对于返回列表的每个元素都是 true。

      请注意,已完成的任务可以正常终止,也可以通过引发异常终止。如果在此操作进行时修改了给定的集合,则此方法的结果是不确定的。

      ForkJoinPoolExecutors.html#newWorkStealingPool 提供了其他替代方法来实现相同的目的。

      示例代码sn-p:

      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;
              }
          }
      }
      

      【讨论】:

      • 我是否理解正确:所有Callables 将并行执行,而不是随后执行?
      【解决方案5】:

      SwingWorker 具有 doInBackground(),您可以使用它来执行任务。您可以选择调用 get() 并等待下载完成,或者您可以覆盖 done() 方法,一旦 SwingWorker 完成,该方法将在事件调度线程上调用。

      Swingworker 与您当前的方法相比具有优势,因为它具有您正在寻找的许多功能,因此无需重新发明轮子。您可以使用 getProgress()setProgress() 方法作为可运行对象上的观察者的替代方法来获取下载进度。我上面提到的done() 方法在worker 完成执行后调用,并在EDT 上执行,这允许您在下载完成后加载数据。

      【讨论】:

        【解决方案6】:

        我想您正在后台线程中调用您的下载,例如 SwingWorker 提供的。如果是这样,那么只需在同一个 SwingWorker 的 doInBackground 方法中按顺序调用您的下一个代码。

        【讨论】:

          【解决方案7】:

          一般来说,当你想等待一个线程完成时,你应该在它上面调用join()

          【讨论】:

            【解决方案8】:

            有什么建议/例子吗?我关注了SwingWorker...代码很乱,我不喜欢这种做法。

            代替等待完成的get(),使用process()setProgress() 来显示中间结果,正如这个简单的example 或这个相关的example 中所建议的那样。

            【讨论】:

              【解决方案9】:

              join() 方法允许一个线程等待另一个线程完成。但是,与 sleep 一样,join 的时间取决于操作系统,因此您不应假设 join 将完全等待您指定的时间。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-02-15
                • 2015-08-29
                • 2010-10-16
                相关资源
                最近更新 更多