java

7取消与关闭

2018-11-02  本文已影响0人  WFitz

概念

中断是必要的

行为好坏的程序的区别

取消任务概念

任务取消原因

任务取消策略

线程中断策略

注:正如任务中应该包含取消策略,线程同样应该包含中断策略

Thread中断相关操作

注1:如果目标线程已经结束,则 isInterrupted 始终返回false!

注2:调用interrupt并不意味着立即停止目标线程正在执行的任务,而只是传递了请求中断信息!

支持中断的阻塞库方法

注:它们响应中断的操作包括:清除中断状态,抛出InterruptedException。也就是说,收到InterruptedException时,中断状态已经为 false !

中断请求的接收者

任务响应中断的两种方式

注1:任务不应该对执行该任务的线程的中断策略做出任何假设,除非该任务被专门设计为服务中运行,并且在这些服务中包含特定的中断策略!

注2:只有实现了线程中断策略的代码才可以屏蔽中断请求,在常规的任务和库代码中都不应该屏蔽中断请求!

如何中断线程

任务对中断的响应

Java中断机制的优点

标准取消操作示例

package cn.weicm.cancel;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * <b>Time:&#9;2018/8/3 13:52</b><br/>
 * <b>Auth:&#9;weicm</b><br/>
 * <br/>
 * <b>Desp:&#9;在外部线程中安排中断</b><br/>
 * <br/>
 * <b>优点:</b><br/>
 * <ul>
 *     <li>能从任务中抛出未检查异常,异常会被timedRun的调用者捕获</li>
 * </ul>
 * <br/>
 * <b>缺点:</b><br/>
 * <ul>
 *     <li>在中断线程时,不了解执行任务的线程的中断策略,因为timedRun可能在任何线程中运行</li>
 *     <li>如果任务不响应中断,那么timedRun会在任务结束时才返回,此时可能已经超过了指定时限,这会对调用者带来负面影响</li>
 * </ul>
 */
public class TimedRun1 {
    private static final ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);

    public static void main(String[] args) {
        try {
            timedRun(() -> {
                System.out.println("Task start ...");
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    System.out.println("Task is canceled!");
                    //假设任务不响应中断,即不抛出InterruptedException,也不通过Thread.interrupt()恢复中断状态,而是直接退出
                    return;
                }
                System.out.println("Task end ...");
            }, 1, TimeUnit.SECONDS);
        } finally {
            ses.shutdown();
        }

    }


    /**
     * <b>Time:&#9;2018/8/3 14:20</b><br/>
     * <b>Auth:&#9;weicm</b><br/>
     * <br/>
     * <b>Desp:&#9;在制定时限内运行任务,超过时限则取消任务</b><br/>
     *
     * @param task 任务
     * @param timeout 时限数量
     * @param unit 时限单位
     */
    static void timedRun(Runnable task, long timeout, TimeUnit unit) {
        //获取目标任务所在线程,此时该线程可能是任意线程
        Thread currentThread = Thread.currentThread();
        //启动中断任务
        ScheduledFuture<?> future = ses.schedule(() -> {
            currentThread.interrupt();
        }, timeout, unit);
        task.run();
        //任务结束后,取消掉中断任务,因为此时目标任务已经结束,中断任务已经没有存在的意义了
        future.cancel(true);
    }
}
package cn.weicm.cancel;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * <b>Time:&#9;2018/8/3 15:36</b><br/>
 * <b>Auth:&#9;weicm</b><br/>
 * <br/>
 * <b>Desp:&#9;在专门的线程中中断任务</b><br/>
 * </br>
 * <b>优点:&#9;</b><br/>
 * <ul>
 *     <li>解决了TimedRun1的所有缺点</li>
 * </ul>
 * </br>
 * <b>缺点:&#9;</b><br/>
 * <ul>
 *     <li>由于join的不足,无法知道任务是因为线程正常退出而返回还是因为join超时而返回</li>
 * </ul>
 */
public class TimedRun2 {
    private static final ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);

    public static void main(String[] args) throws Throwable {
        try {
            timedRun(() -> {
                System.out.println("Task start ...");
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    System.out.println("Task is canceled!");
                    //假设任务不响应中断,即不抛出InterruptedException,也不通过Thread.interrupt()恢复中断状态,而是直接退出
                    return;
                }
                System.out.println("Task end ...");
            }, 1, TimeUnit.SECONDS);
        } finally {
            ses.shutdown();
        }
    }

    /**
     * <b>Time:&#9;2018/8/3 14:20</b><br/>
     * <b>Auth:&#9;weicm</b><br/>
     * <br/>
     * <b>Desp:&#9;在制定时限内运行任务,超过时限则取消任务</b><br/>
     *
     * @param task 任务
     * @param timeout 时限数量
     * @param unit 时限单位
     */
    static void timedRun(Runnable task, long timeout, TimeUnit unit) throws Throwable {
        /**
         * <b>Time:&#9;2018/8/3 14:42</b><br/>
         * <b>Auth:&#9;weicm</b><br/>
         * <br/>
         * <b>Desp:&#9;装执行线程的中断策略</b><br/>
         */
        class ThrowableTask implements Runnable {
            private volatile Throwable e;

            @Override
            public void run() {
                try {
                    task.run();
                } catch (Throwable e) {
                    //中断策略: 录任务的运行时异常,以便稍后重新抛出该异常,并结束执行线程
                    this.e = e;
                }
            }

            /**
             * <b>Time:&#9;2018/8/3 15:33</b><br/>
             * <b>Auth:&#9;weicm</b><br/>
             * <br/>
             * <b>Desp:&#9;重新抛出目标任务运行过程中可能发生的异常</b><br/>
             *
             * @throws Throwable
             */
            public void rethrow() throws Throwable {
                if (null != e)
                    throw e;
            }
        }
        //将目标任务运行在明确中断策略的执行线程里
        ThrowableTask t = new ThrowableTask();
        Thread taskThread = new Thread(t);
        taskThread.start();
        //启动中断任务
        ScheduledFuture<?> future = ses.schedule(() -> {
            taskThread.interrupt();
        }, timeout, unit);
        taskThread.join(unit.toMillis(timeout));
        //任务结束后,取消掉中断任务,因为此时目标任务已经结束,中断任务已经没有存在的意义了
        future.cancel(true);
        //重新抛出任务执行过程中发生的异常
        t.rethrow();
    }
}
package cn.weicm.cancel;

import java.util.concurrent.*;

/**
 * <b>Time:&#9;2018/8/3 16:38</b><br/>
 * <b>Auth:&#9;weicm</b><br/>
 * <br/>
 * <b>Desp:&#9;通过Future来取消任务</b><br/>
 * </br>
 * <b>优点:&#9;</b><br/>
 * <ul>
 *     <li>解决TimedRun2的缺点,可以区分任务是如何结束的</li>
 * </ul>
 * </br>
 * <b>关于Futrue.cancel:&#9;针对任务的三种状态</b><br/>
 * <ul>
 *     <li>等待状态:此时不管参数传入的是true还是false,任务都会被标记为取消,任务依然保存在队列中,但当轮询到此任务时会直接跳过</li>
 *     <li>运行状态:此时参数传入true会中断正在执行的任务;传入false则不会中断任务,而是让任务继续运行直到结束</li>
 *     <li>完成状态:此时不管参数传入的是true还是false,cancel都不起作用,因为任务已经完成了</li>
 * </ul>
 */
public class TimedRun3 {
    private static final ExecutorService es = Executors.newSingleThreadExecutor();
    public static void main(String[] args) throws Exception{
        try {
            timedRun(() -> {
                System.out.println("Task start ...");
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    System.out.println("Task is canceled!");
                    //假设任务不响应中断,即不抛出InterruptedException,也不通过Thread.interrupt()恢复中断状态,而是直接退出
                    return;
                }
                System.out.println("Task end ...");
            }, 1, TimeUnit.SECONDS);
        } finally {
            es.shutdown();
        }
    }

    /**
     * <b>Time:&#9;2018/8/3 14:20</b><br/>
     * <b>Auth:&#9;weicm</b><br/>
     * <br/>
     * <b>Desp:&#9;在制定时限内运行任务,超过时限则取消任务</b><br/>
     *
     * @param task 任务
     * @param timeout 时限数量
     * @param unit 时限单位
     */
    static void timedRun(Runnable task, long timeout, TimeUnit unit) throws Exception {
        Future<?> future = es.submit(task);
        try {
            future.get(timeout, unit);
        } catch (InterruptedException e) {
            //当前线程被中断,恢复中断状态以传递中断信息
            Thread.currentThread().interrupt();
        } catch (ExecutionException e) {
            //目标任务执行过程发生运行时异常,直接抛出
            throw e;
        } catch (TimeoutException e) {
            //目标任务执行超时,接下来取消任务,因为已经不需要结果了
        } finally {
            //如果任务已经运行完,则取消操作不产生任何效果;如果任务由与异常而终止,不管什么异常,则取消任务,因为已经不需要结果了
            //取消那些不在需要结果的任务是一种良好的习惯!
            future.cancel(true);
        }
    }
}

不可中断的阻塞

常见不可中断的阻塞操作

上一篇下一篇

猜你喜欢

热点阅读