Site Search:

TimedRun2.java

Back>

TimedRun2.java fixed the problem of TimedRun1.java. In timedRun method, a local thread is created to run the passed in Runnable, then the calling thread join with the newly created thread. Because timedRun method owns the newly created thread, the timedRun method author know what interruption means to that thread -- that is, because of the join, taskThread.interrupt(); won't go off on a totally irrelevant Runnable as TimedRun1.java do.

Another good feature of TimeRun2.java is, After join returns, it checks if an exception was thrown from the task and if so, re throws it in the thread calling timedRun. The re thrown exception can be InterruptedException of cause. If no exception was thrown from the task, calling task.rethrow(); does nothing.

CHAP7>cat TimedRun2.java 
import java.util.concurrent.*;

/**
 * TimedRun2
 * <p/>
 * Interrupting a task in a dedicated thread
 */
public class TimedRun2 {
    private static final ExecutorService exec = Executors.newFixedThreadPool(2);
    private static final ScheduledExecutorService cancelExec = Executors
            .newScheduledThreadPool(1);

    public static void timedRun(final Runnable r, long timeout, TimeUnit unit)
            throws InterruptedException {
        class RethrowableTask implements Runnable {
            private volatile Throwable t;

            public void run() {
                try {
                    r.run();
                } catch (Throwable t) {
                    this.t = t;
                }
            }

            void rethrow() {
                if (t != null) {
                    System.out.println("thread rethrow the exception: "
                            + Thread.currentThread());
                    throw new RuntimeException(t);
                }
                // do nothing
            }
        }

        RethrowableTask task = new RethrowableTask();
        final Thread taskThread = new Thread(task);
        taskThread.start();
        cancelExec.schedule(new Runnable() {
            public void run() {
                System.out
                        .println("cancelExec thread do the delayed interrupt: "
                                + Thread.currentThread());
                taskThread.interrupt();
            }
        }, timeout, unit);
        taskThread.join(unit.toMillis(timeout));
        task.rethrow();
    }

    public static void main(String... args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("worker thread run runnable1: "
                            + Thread.currentThread());
                    // Runnable r2 = () ->
                    // System.out.println(Thread.currentThread() +
                    // ": suppose to interrupt me.");
                    Runnable r2 = () -> {
                        int i = 10 / 0;
                    };
                    // Runnable r2 = () -> {
                    // try {
                    // System.out.println("new thread run submitted runnable: "
                    // + Thread.currentThread());
                    // Thread.sleep(5000);
                    // } catch(Exception e) {
                    // e.printStackTrace();
                    // }
                    // };

                    timedRun(r2, 1, TimeUnit.SECONDS);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        exec.submit(r);

        exec.submit(() -> {
            try {
                System.out.println("worker thread run runnable2: "
                        + Thread.currentThread());
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread()
                        + ": should no longer be a potential victim!");
                e.printStackTrace();
            }
        });

        exec.submit(() -> {
            try {
                System.out.println("worker thread run runnable3: "
                        + Thread.currentThread());
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread()
                        + ": I should not be interrupted!");
                e.printStackTrace();
            }
        });

        try {
            exec.shutdown();
            exec.awaitTermination(10, TimeUnit.SECONDS);
            cancelExec.shutdown();
            cancelExec.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
CHAP7>javac TimedRun2.java 
CHAP7>java TimedRun2
worker thread run runnable1: Thread[pool-1-thread-1,5,main]
worker thread run runnable2: Thread[pool-1-thread-2,5,main]
thread rethrow the exception: Thread[pool-1-thread-1,5,main]
java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
at TimedRun2$1RethrowableTask.rethrow(TimedRun2.java:30)
at TimedRun2.timedRun(TimedRun2.java:48)
at TimedRun2$2.run(TimedRun2.java:74)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ArithmeticException: / by zero
at TimedRun2$2.lambda$run$0(TimedRun2.java:62)
at TimedRun2$2$$Lambda$1/305979380.run(Unknown Source)
at TimedRun2$1RethrowableTask.run(TimedRun2.java:20)
... 1 more
worker thread run runnable3: Thread[pool-1-thread-1,5,main]
cancelExec thread do the delayed interrupt: Thread[pool-2-thread-1,5,main]

CHAP7>