I got a fixed number of threads. I want each thread to run three Runnable
s, one after another. Here's some pseudocode to explain:
Thread[] threads = new Thread[4];
for (int i = 0; i < threads.length; i++) {
// Set the first tasks.
threads[i] = new Thread(new FirstRunnable());
threads[i].start();
}
for (int i = 0; i < threads.length; i++)
threads[i].join(); // wait until the first tasks are done
for (int i = 0; i < threads.length; i++) {
// Set the second task.
threads[i].setRunnable(new SecondRunnable());
threads[i].start();
}
for (int i = 0; i < threads.length; i++)
threads[i].join(); // wait until the second tasks are done
...
Using a ThreadPool
sounds way overkill, especially since I'm headed for performance, performance, performance. What's the best way to implement this in Java?
Whenever you see new Thread(...).start()
, make use of the Executors
framework. In particular, make use of Executors.newFixedThreadPool(...)
.
You can use a CyclicBarrier and a "CombinedRunnable" as shown below. The barrier allows the threads to all wait for each other to finish, before proceeding to the next runnable.
CyclicBarrier barrier = new CyclicBarrier(4);
Runnable r = new CombinedRunnable(barrier, new FirstRunnable(), new SecondRunnable());
Thread[] threads = new Thread[4];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(r);
threads[i].start();
}
The CombinedRunnable class:
public class CombinedRunnable implements Runnable{
private final CyclicBarrier barrier;
private final Runnable[] runnables;
public CombinedRunnable(CyclicBarrier barrier, Runnable... runnables){
this.barrier = barrier;
this.runnables = runnables;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
for(Runnable r: runnables){
r.run();
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
Seems like a good use for a newFixedThreadPool from the Executors class.
So your code would look something like:
ExecutorService es = Executors.newFixedThreadPool(4);
List<Future> futures = new ArrayList<Future>();
for (int x = 0; x < 4; x ++) {
futures.add(es.submit(new FirstRunnable()));
}
while (futures.size() > 0) {
futures.remove(0).get();
}
for (int x = 0; x < 4; x ++) {
futures.add(es.submit(new SecondRunnable()));
}
while (futures.size() > 0) {
futures.remove(0).get();
}
Of course, you could probably easily refactor the code above to remove code duplication.
An idiomatic way to achieve this is by using an Executor
in conjunction with a CompletionService
. This allows you to map many units of work to a fixed size pool of threads and also provides an elegant mechanism for blocking until all work is complete.
Note that your concern about how using a thread pool might impact efficiency is not really an issue: The main overhead is in creating individual threads, which you were doing anyway; the additional object creation overhead in creating a pool will be negligible.
// Create fixed thread pool and wrap in a CompletionService to allow for easy access to completed tasks.
// We don't have an explicit result for each Runnable so parameterise the service on Void.
CompletionService<Void> cs = new ExecutorCompletionService<Void>(Executors.newFixedThreadPool(3));
// Create units of work for submission to completion service.
Runnable[] runnables = ...
// Submit runnables. Note that we don't care about the result so pass in null.
for (Runnable r : runnables) {
cs.submit(r, null);
}
// Take each *completed* result in turn, blocking until a completed result becomes available.
for (int i=0; i<runnables.length; ++i) {
Future<Void> completed = cs.take();
}
Executor Framework is just for you.
Here's the pseudocode:
1. Create executor service
Executors type1Runnables = Executors.newFixedThreadPool(4);
Executors type2Runnables = Executors.newFixedThreadPool(4);
etc..
2. Submit tasks to it
for(){
type1Runnables.submit(new Runnable1());
type2Runnables.submit(new Runnable2);
}
3. Invoke the executors
type1Runnables.invokeAll();
type2Runnables.invokeAll();
To make it more generic you could perhaps write your own executorservicefactory which accepts the different runnable types.
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.