Reputation: 1
I have a pausable thread pool executor implementation just like in the documentation of the ThreadPoolExecutor class. I have a simple test that does the following:
class PausableThreadPoolExecutor extends ThreadPoolExecutor {
public static PausableThreadPoolExecutor newSingleThreadExecutor() {
return new PausableThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/** isPaused */
private boolean isPaused;
/** pauseLock */
private ReentrantLock pauseLock = new ReentrantLock();
/** unpaused */
private Condition unpaused = this.pauseLock.newCondition();
public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
this.pauseLock.lock();
try {
while (this.isPaused) {
this.unpaused.await();
}
} catch (InterruptedException ie) {
t.interrupt();
} finally {
this.pauseLock.unlock();
}
}
public void pause() {
this.pauseLock.lock();
try {
this.isPaused = true;
} finally {
this.pauseLock.unlock();
}
}
public void resume() {
this.pauseLock.lock();
try {
this.isPaused = false;
this.unpaused.signalAll();
} finally {
this.pauseLock.unlock();
}
}
public static void main(String[] args) {
PausableThreadPoolExecutor p = PausableThreadPoolExecutor.newSingleThreadExecutor();
p.pause();
p.execute(new Runnable() {
public void run() {
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
System.out.println(ste);
}
}
});
p.shutdownNow();
}
}
Interestingly the call to shutDownNow will cause the Runnable to run. Is this normal? As I understand the shutDownNow will try to stop the actively executing tasks by interrupting them. But the interrupt seems to wake up the task an execute it. Can someone explain this ?
Upvotes: 0
Views: 445
Reputation: 116888
Interestingly the call to shutDownNow will cause the Runnable to run. Is this normal?
Not sure it is "normal" but it is certainly expected given your code. In your beforeExecute(...)
method I see the following:
this.pauseLock.lock();
try {
while (this.isPaused) {
this.unpaused.await();
}
} catch (InterruptedException ie) {
t.interrupt();
} finally {
this.pauseLock.unlock();
}
The job will lopp waiting for the isPaused
boolean to be set to false. However, if the job is interrupted the this.unpaused.await()
will throw InterruptedException
which breaks out of the while
loop, the thread is reinterrupted which is always a good pattern, beforeExecute()
returns, and the job is allowed to execute. Interrupting a thread doesn't kill it unless you have specific code to handle the interruption.
If you want to stop the job when it is interrupted then you could throw a RuntimeException
in the beforeExecute()
handler when you see that the job as been interrupted:
} catch (InterruptedException ie) {
t.interrupt();
throw new RuntimeException("Thread was interrupted so don't run");
A cleaner approach might be to check to see if you are interrupted in the run()
method and then exit:
public void run() {
if (Thread.currentThread().isInterrupted()) {
return;
}
...
Upvotes: 1