Skip to main content
Fixed spelling, added some additional info about what to override
Source Link
impl
  • 803
  • 6
  • 14

The solution I'm pondering goes something like this, using an atomic counter like in Eng.Fuoad'sFouad's answer and a Condition for signaling the main thread to wake up (pardon the shortcuts for simplicity):

Edit: On second thought, incrementing the atomic counter should happen upon submission, not immediately before task execution (because queuing could cause the counter to fall to 0 prematurely). It probably makes sense to override the submit(...) methods instead, and possibly also remove(...) and shutdown() (if you use thatthem). The general idea remains the same, though. (But the more I think about it, the less pretty it is.)

The solution I'm pondering goes something like this, using an atomic counter like in Eng.Fuoad's answer and a Condition for signaling the main thread to wake up (pardon the shortcuts for simplicity):

Edit: On second thought, incrementing the atomic counter should happen upon submission, not immediately before task execution (because queuing could cause the counter to fall to 0 prematurely). It probably makes sense to override the submit(...) methods instead, and possibly also remove(...) (if you use that). The general idea remains the same, though.

The solution I'm pondering goes something like this, using an atomic counter like in Eng.Fouad's answer and a Condition for signaling the main thread to wake up (pardon the shortcuts for simplicity):

Edit: On second thought, incrementing the atomic counter should happen upon submission, not immediately before task execution (because queuing could cause the counter to fall to 0 prematurely). It probably makes sense to override the submit(...) methods instead, and possibly also remove(...) and shutdown() (if you use them). The general idea remains the same, though. (But the more I think about it, the less pretty it is.)

Add some additional thoughts
Source Link
impl
  • 803
  • 6
  • 14

Edit: On second thought, incrementing the atomic counter should happen upon submission, not immediately before task execution (because queuing could cause the counter to fall to 0 prematurely). It probably makes sense to override the submit(...) methods instead, and possibly also remove(...) (if you use that). The general idea remains the same, though.

I'd also check out the internals of the class to see if you can glean any knowledge from it: http://hg.openjdk.java.net/build-infra/jdk7/jdk/file/0f8da27a3ea3/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java. The tryTerminate() method looks interesting.


Edit: On second thought, incrementing the atomic counter should happen upon submission, not immediately before task execution (because queuing could cause the counter to fall to 0 prematurely). It probably makes sense to override the submit(...) methods instead, and possibly also remove(...) (if you use that). The general idea remains the same, though.

I'd also check out the internals of the class to see if you can glean any knowledge from it: http://hg.openjdk.java.net/build-infra/jdk7/jdk/file/0f8da27a3ea3/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java. The tryTerminate() method looks interesting.

Source Link
impl
  • 803
  • 6
  • 14

Since the last task doesn't know that it's the last, I actually don't think it's possible to have this work 100% correctly without recording both when tasks launch and when they complete.

If memory serves me right, the getQueue() method returns a queue containing only tasks that are still waiting to be executed, not ones that are currently running. Furthermore, getCompletedTaskCount() is approximate.

The solution I'm pondering goes something like this, using an atomic counter like in Eng.Fuoad's answer and a Condition for signaling the main thread to wake up (pardon the shortcuts for simplicity):

public class MyThreadPoolExecutorState { public final Lock lock = new ReentrantLock(); public final Condition workDone = lock.newCondition(); public boolean workIsDone = false; } public class MyThreadPoolExecutor extends ThreadPoolExecutor { private final MyThreadPoolExecutorState state; private final AtomicInteger counter = new AtomicInteger(0); public MyThreadPoolExecutor(MyThreadPoolExecutorState state, ...) { super(...); this.state = state; } protected void beforeExecute(Thread t, Runnable r) { this.counter.incrementAndGet(); } protected void afterExecute(Runnable r, Throwable t) { if(this.counter.decrementAndGet() == 0) { this.state.lock.lock(); try { this.state.workIsDone = true; this.state.workDone.signal(); } finally { this.state.lock.unlock(); } } } } public class MyApp { public static void main(...) { MyThreadPoolExecutorState state = new MyThreadPoolExecutorState(); MyThreadPoolExecutor executor = new MyThreadPoolExecutor(state, ...); // Fire ze missiles! executor.submit(...); state.lock.lock(); try { while(state.workIsDone == false) { state.workDone.await(); } } finally { state.lock.unlock(); } } } 

It could be a little more elegant (maybe just provide a getState() in your thread pool executor or something?), but I think it should get the job done. It's also untested, so implement at your own peril...

It is worth noting that this solution will definitely fail if there are no tasks to be executed -- it'll await the signal indefinitely. So don't even bother starting the executor if you have no tasks to run.