FULL PRODUCT VERSION :
JDK 1.7.0_02
ADDITIONAL OS VERSION INFORMATION :
Windows 7 x64
A DESCRIPTION OF THE PROBLEM :
When using a subclassed ScheduledThreadPoolExecutor with the example afterExecute method provided in the documentation, the implementation will hang on the
Object result = ((Future<?>) r).get();
line. The reason is as follows:
for a scheduledThreadPool afterExecute is called after every timed execution ( this is possible another bug as various docs on the internet imply it is only called after the task is cancelled or fully completed, but that is another issue) however, the call to get() tries to get a shared lock which only succeeds once the task has completed. Therefore afterExecute hangs on this lock indefinitely as the task is rescheduled for another execution.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The provided code fails each time it is run
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The program should continue to print "something" every 1 second for ever (or until stopped)
ACTUAL -
something is printed 5 times (once for each thread in the pool) before hanging indefinitely.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package tasktests;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TaskTests {
private static class MyExecutor extends ScheduledThreadPoolExecutor {
public MyExecutor(){
super(5);
}
protected void afterExecute(Runnable r, Throwable t){
super.afterExecute(r, t);
if (t == null && r instanceof Future<?> && ((Future<?>)r).isDone()) {
try {
Object result = ((Future<?>) r).get(); // << hangs here
result=null;
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
}
if (t != null) {
System.out.println(t);
}
}
}
private static class FailTask implements Runnable {
public void run(){
System.out.println("something");
}
}
public static void main(String[] args) {
exec=Executors.newScheduledThreadPool(5);
ScheduledExecutorService exec=new MyExecutor();
exec.scheduleWithFixedDelay(new FailTask(), 0, 1000, TimeUnit.MILLISECONDS);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
A work around is to put a call to isDone() in the code before calling get as follows:
protected void afterExecute(Runnable r, Throwable t){
super.afterExecute(r, t);
if (t == null && r instanceof Future<?> && ((Future<?>)r).isDone()) {
try {
Object result = ((Future<?>) r).get()
result=null;
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
}
if (t != null) {
System.out.println(t);
}
}