I have a thread downloading data and I want to wait until the download is finished before I load the data. Is there a standard way of doing this?
More Info:
I have a Download class that gets data from a URL (Serialized POJOs). Download is Runnable and Observable. It keeps track of the bytes downloaded and download size. I have a progress bar that displays the progress to the User. The GUI observes Download to update the progress bar.
When the POJO is downloaded I want to get it and move to the next step. Each step has to wait for the previous to finish. The problem is I cant think of a way to pause my application to wait for the download thread. Once the download is finished I want to call download.getObject()
which will return the data as an object. I can then cast it and get on with the next download.
I have a helper class that manages the URLs for download and makes all of the calls to Download. This call will call getObject and do the casting. The Gui calls开发者_如何学运维 helper.getUser()
. helper starts the thread running and I want it to 'know' when it is finished so it can return the casted object.
Any suggestions/examples? I am in the beginning stages of this design so I am willing to change it.
Update:
I followed http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get and used modal to block until the thread finished. The code was very messy and I don't like this approach. I will keep trying to find a 'clean' way to handle the workflow of the download processes.
Thread
has a method that does that for you join which will block until the thread has finished executing.
You could use a CountDownLatch
from the java.util.concurrent
package. It is very useful when waiting for one or more threads to complete before continuing execution in the awaiting thread.
For example, waiting for three tasks to complete:
CountDownLatch latch = new CountDownLatch(3);
...
latch.await(); // Wait for countdown
The other thread(s) then each call latch.countDown()
when complete with the their tasks. Once the countdown is complete, three in this example, the execution will continue.
You can use join()
to wait for all threads to finish. Like below:
for (int i = 0; i < 10; i++)
{
Thread T1 = new Thread(new ThreadTest(i));
T1.start();
try {
T1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Better alternatives to join() method have been evolved over a period of time.
ExecutorService.html#invokeAll is one alternative.
Executes the given tasks, returning a list of Futures holding their status and results when all complete. Future.isDone() is true for each element of the returned list.
Note that a completed task could have terminated either normally or by throwing an exception. The results of this method are undefined if the given collection is modified while this operation is in progress.
ForkJoinPool or Executors.html#newWorkStealingPool provides other alternatives to achieve the same purpose.
Example code snippet:
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 has doInBackground()
which you can use to perform a task. You have the option to invoke get()
and wait for the download to complete or you can override the done()
method which will be invoked on the event dispatch thread once the SwingWorker completes.
The Swingworker has advantages to your current approach in that it has many of the features you are looking for so there is no need to reinvent the wheel. You are able to use the getProgress()
and setProgress()
methods as an alternative to an observer on the runnable for download progress. The done()
method as I stated above is called after the worker finishes executing and is performed on the EDT, this allows you load the data after the download has completed.
I imagine that you're calling your download in a background thread such as provided by a SwingWorker. If so, then simply call your next code sequentially in the same SwingWorker's doInBackground method.
Generally, when you want to wait for a thread to finish, you should call join() on it.
Any suggestions/examples? I followed
SwingWorker
... The code was very messy and I don't like this approach.
Instead of get()
, which waits for completion, use process()
and setProgress()
to show intermediate results, as suggested in this simple example or this related example.
The join() method allows one thread to wait for the completion of another.However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.
精彩评论