简体   繁体   中英

Waiting list of threads in java

I'm writing a swing application with HttpClient and I need a way to make a download list because I need to wait 1 minute (for example) before starting a new download.

So I would like to create a waiting list of threads (downloads). I would have a class that takes a time parameter and contains a list of threads and when I add a thread in the list it starts if there is no running thread. Otherwise it waits for its turn.

Is there any tool to do that ?

Thanks a lot for your help.

Yes. ScheduledExecutorService . You can create a fixed length service via Executors.newScheduledThreadPool(corePoolSize) . When you are ready to submit the task to wait the amount of time just submit it to ScheduledExecutorService.schedule

ScheduledExecutorService e = Executors.newScheduledThreadPool(10)
private final long defaultWaitTimeInMinutes = 1;
public void submitTaskToWait(Runnable r){
    e.schedule(r, defaultWaitTimeInMinutes, TimeUnit.MINUTES);
}

Here the task will launch in 1 minute from the time of being submitted. And to address your last point. If there are currently tasks being downloaded (this configuration means 10 tasks being downloaded) after the 1 minute is up the runnable submitted will have to wait until one of the other downloads are complete.

Keep in mind this deviates a bit from the way you are designing it. For each new task you wouldnt create a new thread, rather you would submit to a service that already has thread(s) waiting. For instance, if you only want one task to download at a time you change from Executors.newScheduledThreadPool(10) to Executors.newScheduledThreadPool(1)

Edit: I'll leave my previous answer but update it with a solution to submit a task to start exactly 1 minute after the previous task completes. You would use two ExecutorServices. One to submit to the scheuled Executor and the other to do the timed executions. Finally the first Executor will wait on the completion and continue with the other tasks queued up.

ExecutorService e = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(1)
public void submitTask(final Runnable r){
    e.submit(new Runnable(){
       public void run(){
           ScheduledFuture<?> future= scheduledService.schedule(r, defaultWaitTimeInMinutes, TimeUnit.MINUTES);
           future.get(); 

       }
    });
}

Now when the future.get(); completes the next Runnable submitted through submitTask will be run and then scheduled for a minute. Finally this will work only if you require the task to wait the 1 minute even if there is no other tasks submitted.

I think this would be a wrong way of going about the problem. A bit more logical way would be to create "download job" objects which will be added to a job queue. Create a TimerTask which would query this "queue" every 1 minute, pick up the Runnable / Callable jobs and submit them to the ExecutorService .

You could use the built-in ExecutorService . You can queue up tasks as Runnable s and they will run on the available threads. If you want only a single task to run at a time use newFixedThreadPool(1);

ExecutorService executor = Executors.newFixedThreadPool(1);

You could then append an artificial Thread.sleep at the beginning of each Runnable run method to ensure that it waits the necessary amount of time before starting (not the most elegant choice, I know).

The Java Concurrency package contains classes for doing what you ask. The general construct you're talking about is an Executor which is backed by a ThreadPool. You generate a list of Runables and send them to an Executor. The Executor has a ThreadPool behind it which will run the Runnables as the threads become available.

So as an example here, you could have a Runnable like:

  private static class Downloader implements Runnable {

    private String file;

    public Downloader(String file) {
      this.file = file;
    }

    @Override
    public void run() {
      // Use HttpClient to download file.
    }
  }

Then You can use it by creating Downloader objects and submitting it to an ExecutorService:

  public static void main(String[] args) throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    for (String file : args) {
      executorService.submit(new Downloader(file));
    }
    executorService.awaitTermination(100, TimeUnit.SECONDS);
  }

It is maybe not the best solution but here is what I came up with thanks to the answer of John Vint. I hope it will help someone else.

package tests;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


public class RunnableQueue 
{
    private long waitTime;
    private TimeUnit unit;

    ExecutorService e;

    public RunnableQueue(long waitTime, TimeUnit unit) {
        e = Executors.newSingleThreadExecutor();

        this.waitTime = waitTime;
        this.unit = unit;
    }

    public void submitTask(final Runnable r){
        e.submit(new Runnable(){
           public void run(){
               Thread t = new Thread(r);
               t.start();

               try {
                t.join();
                Thread.sleep(unit.toMillis(waitTime));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
           }
        });
    }

    public static void main(String[] args) {
        RunnableQueue runQueue = new RunnableQueue(3, TimeUnit.SECONDS);

        for(int i=1; i<11; i++)
        {
            runQueue.submitTask(new DownloadTask(i));
            System.out.println("Submitted task " + i);
        }

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM