这听起来像是一个理想的用例ExecutorService.invokeAll
:
ExecutorService pool = Executors.newCachedThreadPool();
List<Callable<Object>> tasks = new ArrayList<Callable<Object>>();
for(int i = 0; i < myList.size(); ++i) {
tasks.add (Executors.callable(new ThreadProcessRunnable (args)));
}
List<Future<Object>> futures = pool.invokeAll(tasks);
System.out.println("All tasks finished");
public class ThreadProcessRunnable implements Runnable {
public void run() {
// do some stuff
}
}
invokeAll
阻塞,直到提供的所有任务List
都完成。
If you absolutely must have the println
inside one of the threads' run
methods, then the simplest approach I can think of would be to keep some sort of counter in an AtomicInteger
public class ThreadProcessRunnable implements Runnable {
private AtomicInteger taskCounter;
public ThreadProcessRunnable(AtomicInteger counter) {
this.taskCounter = counter;
}
public void run() {
// do stuff
if(taskCounter.decrementAndGet() == 0) {
System.out.println("I am the last thread and I am about to finish");
}
}
}
// Main class
ExecutorService pool = Executors.newCachedThreadPool();
AtomicInteger taskCounter = new AtomicInteger(myList.size());
for(int i = 0; i < myList.size(); ++i) {
pool.execute(new ThreadProcessRunnable(taskCounter));
}
The key thing that makes this work is that taskCounter.decrementAndGet
is atomic - if the value of taskCounter
is initially 2, for example, and two different threads call decrementAndGet
at the same time then it is guaranteed that one thread will see the value 1 and the other thread will see the value 0, so exactly one thread will print the "about to finish" message. This is different from MadProgrammer's answer, which involves a race condition:
latch.countDown();
if(latch.getCount() == 0) { ... }
where it is possible to have thread 1 decrement the value (to 1), then thread 2 decrement it again (to 0), then both threads see the value 0 when they call getCount
and both print the message.