Site Search:

OutOfTime.java

Timer creates only a single thread for executing timer tasks, as a result, the timer tasks can step on each other. In this example, after main thread created Timer thread and scheduled the 1st and 2nd TimerTasks, the main thread goes to sleep for 1 second. 1 second later, main thread wake up and scheduled the 3rd TimerTask, then went to sleep. While main thread is sleeping, TimerTask2 throws a RuntimeException, which terminates the timer thread. In that case, TimerTasks that are already scheduled but not yet executed are never run and new TimerTasks can not be submitted. (Since Timer thread is terminated, TimerTasks that are scheduled and run, but haven't finished, dead on the feet.)

Notice System.out.println cross threads boundary will change the synchronization structure of your program, if you add any System.out inside the TimerTask, the observation will be a heisenbug (heisenberg) observation.
https://www.youtube.com/watch?v=yfZEdKljkYM


Concurrency>cat OutOfTime.java 
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * OutOfTime
 * <p/>
 * Class illustrating confusing Timer behavior
 */

public class OutOfTime {
    private static AtomicInteger marker = new AtomicInteger(0);
    public static void main(String[] args) {
        try{
            Timer timer = new Timer();
            System.out.println("schedule task1:" + marker.get());
            timer.schedule(new ThrowTask(), 10);
            System.out.println("schedule task2:" + marker.get());
            timer.schedule(new ThrowTask(), 0);
            SECONDS.sleep(1);
            System.out.println("schedule task3:" + marker.get());
            timer.schedule(new ThrowTask(), 0);
            SECONDS.sleep(2);
            System.out.println("schedule task4:" + marker.get());
            timer.schedule(new ThrowTask(), 1);
            SECONDS.sleep(2);
            System.out.println("schedule task5:" + marker.get());
            timer.schedule(new ThrowTask(), 1);
            SECONDS.sleep(5);
        } catch(Exception e) {
            e.printStackTrace();
        }
        System.out.println("scheduled tasks:" + marker.get());
        
    }

    static class ThrowTask extends TimerTask {
        public void run() {
            //any System.out in Timer thread will add an extra happens-before relationship to main thread
            try {
                SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            marker.set(marker.get() + 1); 
            throw new RuntimeException();
        }
    }
}
Concurrency>javac OutOfTime.java 
Concurrency>java OutOfTime
schedule task1:0
schedule task2:0
schedule task3:0
Exception in thread "Timer-0" java.lang.RuntimeException
at OutOfTime$ThrowTask.run(OutOfTime.java:46)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
schedule task4:1
java.lang.IllegalStateException: Timer already cancelled.
at java.util.Timer.sched(Timer.java:397)
at java.util.Timer.schedule(Timer.java:193)
at OutOfTime.main(OutOfTime.java:25)
scheduled tasks:1

Concurrency>



ScheduledThreadPoolExecutor deals properly with ill-behaved tasks; there is little reason to use Timer in java 5.0 or later. In the following example, Executors.newScheduledThreadPool(2) returns a ScheduledExecutorService. ScheduledExecutorService extends ExecutorService with extra support for schedule capability. After main thread submitted 5 Runnable to ScheduledExecutorService, the main thread then shut down the service. At the time the 6th Runnable is submitted, there are 3 Runnable has finished executing, there are one Runnable that is active, another one (task1 with delay 10 seconds) waiting to be executed. After the ScheduledExecutorService shutdown, it has executed all the submitted Runnables, and rejected any new Runnable submitted after shutdown() is called.




Concurrency>cat OutOfTimeFix.java 
import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class OutOfTimeFix {
    private static AtomicInteger marker = new AtomicInteger(0);
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        System.out.println("schedule task1:" + marker.get());
        service.schedule(new ThrowTask(), 10, TimeUnit.SECONDS);
        System.out.println("schedule task2:" + marker.get());
        service.schedule(new ThrowTask(), 0, TimeUnit.SECONDS);
        SECONDS.sleep(1);
        System.out.println("schedule task3:" + marker.get());
        service.schedule(new ThrowTask(), 0, TimeUnit.SECONDS);
        SECONDS.sleep(2);
        System.out.println("schedule task4:" + marker.get());
        service.schedule(new ThrowTask(), 1, TimeUnit.SECONDS);
        SECONDS.sleep(2);
        System.out.println("schedule task5:" + marker.get());
        ScheduledFuture<?> fu = service.schedule(new ThrowTask(), 1, TimeUnit.SECONDS);
        SECONDS.sleep(3);
        service.shutdown();
        System.out.println("schedule task6:" + marker.get());
        try {
            service.schedule(new ThrowTask(), 1, TimeUnit.SECONDS);
        } catch (RejectedExecutionException e) {
            e.printStackTrace();
        }
        
        service.awaitTermination(10, TimeUnit.SECONDS);
        System.out.println("end of schedule tasks:" + marker.get());
        System.out.println(fu.isDone());
    }

    static class ThrowTask implements Runnable {
        public void run() {
            //any System.out in Timer thread will add an extra happens-before relationship to main thread
            try {
                SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            marker.set(marker.get() + 1); 
            throw new RuntimeException();
        }
    }
}

Concurrency>javac OutOfTimeFix.java 
Concurrency>java OutOfTimeFix
schedule task1:0
schedule task2:0
schedule task3:0
schedule task4:2
schedule task5:2
schedule task6:3
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@75b84c92 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@6bc7c054[Shutting down, pool size = 2, active threads = 1, queued tasks = 1, completed tasks = 3]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:326)
at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:533)
at OutOfTimeFix.main(OutOfTimeFix.java:30)
end of schedule tasks:5
true

Concurrency>