2011-01-14 219 views
36

我有一個線程下載數據,我想等到下載完成後才加載數據。有沒有這樣做的標準方式?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和使用模式來阻止,直到線程完成。代碼非常混亂,我不喜歡這種方法。我將繼續努力尋找一種「乾淨」的方式來處理下載過程的工作流程。

回答

51

Thread有一個方法可以幫你。 join將阻塞,直到線程完成執行。

+0

我想加盟(),但保留了GUI的更新。我使用線程的原因是在下載過程中保持GUI更新。調用加入停止發生。 – Allan 2011-01-14 13:41:19

+4

join()將起作用。如果GUI停止更新,則可能必須更詳細地說明要加入的線程。我不會加入美國東部時間。 – akf 2011-01-14 13:49:59

+2

然後你需要確保你沒有在swing線程中調用`join()`。你可以通過創建一個負責你的下載線程的線程來做到這一點。這基本上只是一個你可以忘記的背景工作者。工作線程知道何時下載完成以及該做什麼。你只需要確保擺動對象的編輯是在擺動線程中完成的。 – unholysampler 2011-01-14 13:50:36

3

通常,當你想等待一個線程完成時,你應該打電話給join()

4

我想象你在後臺線程中調用你的下載,例如由SwingWorker提供。如果是這樣,那麼只需在同一個SwingWorker的doInBackground方法中依次調用您的下一個代碼即可。

10

SwingWorkerdoInBackground()您可以使用它來執行任務。您可以選擇預製get()並等待下載完成,或者您可以覆蓋done()方法,一旦Swingworker完成,該方法將在事件分派線程上執行。

Swingworker具有您當前的方法的優點,因爲它具有許多您正在尋找的功能,所以不需要重新發明輪子。您可以使用getProgress() & setProgress()方法作爲可運行下載進度的觀察者的替代方法。如上所述,done()方法在worker完成執行後調用,並在EDT上執行,這允許您在下載完成後加載數據。

1

任何建議/示例?我跟着SwingWorker ...代碼非常混亂,我不喜歡這種方法。

代替get(),它等待完成,使用process()setProgress()顯示中間結果,如以下簡單example或該相關example建議。

36

您可以使用java.util.concurrent包中的CountDownLatch。當在等待線程中繼續執行之前,等待一個或多個線程完成時非常有用。

例如,等了三個任務來完成:

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

另一個線程(或多個),然後每次調用latch.countDown()當與他們的任務完成了。一旦倒數完成,在這個例子中三個,執行將繼續。

0

join()方法允許一個線程等待另一個線程的完成。但是,與睡眠一樣,連接依賴於操作系統進行計時,因此您不應該認爲連接將按照您指定的時間完全等待。

1

join()方法的更好的替代方法已經發展了一段時間。

ExecutorService.html#invokeAll是一種選擇。

執行給定的任務,返回持有其狀態和結果的期貨清單,當所有完成。 Future.isDone()對於返回列表的每個元素都是true。

請注意,已完成的任務可能已正常結束或通過拋出異常終止。如果在進行此操作時修改了給定集合,則此方法的結果未定義。

ForkJoinPoolExecutors.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