• 《Java并发编程实战》(六)---- 取消与关闭


      任务和线程的启动很容易。然而,有时候我们希望提前结束任务或线程,或许是因为用户取消了操作,或者应用程序需要被快速关闭。

      要使任务和线程能安全/快速/可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程,但它提供了中断,这是一种协作机制,能够使一个线程终止另一个线程的工作。

      1,任务取消

      如果外部代码能在某个操作正常完成之前将其置入“完成”状态,那么这个操作就可以称为可取消的。

    协作机制能设置某个标志位,任务会定期查看这个标志,如果设置了标志,那么任务将提前结束。

    2,线程中断

    线程中断是一种协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的活着可能的情况下停止当前工作,并转而执行其他的工作。

    每个线程都有一个boolean类型的中断状态。当中断线程时,这个线程的中断状态将被设置为true。在Thread中包涵了中断线程以及查询线程中断状态的方法。interrupt方法能中断目标线程,而isInterrupted方法能返回目标线程的中断状态,静态的interrupted方法将清除当前的中断状态,并返回它之前的值。

    阻塞库方法,例如Thread.sleep()、Object.wait()、Therad.join()等,都会去检查线程何时中断,并且在发现中断时提前返回。他们在响应中断时执行的操作包括: 清除中断状态、抛出InterruptedException,表示阻塞操作由于中断而提前结束。

    调用interrupt并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的消息。然后由线程在下一个合适的时刻中断自己。

    3,通过Future.cancel()来实现取消

    public static void timeRun(Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
        Future<?> task = taskExec.submit(t);
        try {
            task.get(timeout, unit);
        } catch (TimeoutException e) {
        } catch (ExecutionException e) {
        
            throw launderThrowable(e.getCause());
        } finally {
            task.cancel(true);
        }
    }

    当Future抛出InterruptedException或TimeoutException时,如果知道不再需要结果,那么就可以调用Future.cancel来取消任务 。

    ,4,处理不可中断的阻塞

    并非所有的可阻塞方法或者阻塞机制都能响应中断;如果一个线程由于执行IO或者等待获得内置锁而阻塞,那么请求只能设置线程的中断状态,除此之外没有任何其他作用。对于那些执行不可中断操作而被阻塞的线程,可以使用类似于中断的手段来停止这些线程,但这要求我们必须知道线程阻塞的原因 。

    Java.io包中的同步Socket I/O: 虽然InputStream和OutputStream中的read和write不会响应中断,但通过关闭底层的套接字,可以使得由于执行read或write方法而阻塞的线程抛出一个SocketException。

    获取某个锁:如果一个线程由于等待某个内置锁而阻塞,那么将无法响应中断,因为线程认为它肯定会获得锁,所以不会理会中断请求。但是Lock类中提供了lockInterruptibly方法,该方法允许在等待一个锁的同事仍能响应中断。

    所以知道了线程阻塞的原因,还是可以通过捕获异常或者调用自定义中断方法来进行取消操作。

    5,采用newTaskFor来封装非标准的取消

    newTaskFor是Java 6在ThreadPoolExecutor中的新增功能。当把一个Callable提交给ExecutorService时,submit方法会返回一个Future,可以通过这个Future来取消任务。newTaskFor是一个工厂方法,它将创建Future来代表任务,newTaskFor还能返回一个RunnableFuture接口,该接口扩展了Future和Runnable,并由FutureTask实现。

    6,处理非正常的线程终止

    导致线程提前死亡的最主要原因是RuntimeException。由于这些异常表示出现了某种编程错误或者其他不可修复的错误,因此它们通常不会被捕获。它们不会在调用栈中逐层传递,而是默认地在控制台输出栈追踪消息,并终止线程。

  • 相关阅读:
    String类的常用成员方法
    String类和StringBuffer类
    记事本
    GridLayout 可使容器中的各个组件呈网格状布局
    在使用add()方法添加组件到容器时,必须指定将其放置在哪个区域中
    每个容器都有一个布局管理器
    框架一般用作Java应用程序的窗口,而Applet是Java小程序的窗口
    面板是一种透明的容器,没有标题和边框
    要生成一个窗口,通常使用Window的子类Frame类进行实例化
    分布式系统 (大规模分布式系统原理解析和架构实践)
  • 原文地址:https://www.cnblogs.com/IvySue/p/6955260.html
Copyright © 2020-2023  润新知