在历史上,Java试图提供过抢占式限制中断,但问题多多,例如前文介绍的已被废弃的Thread.stop、Thread.suspend和 Thread.resume等。另一方面,出于Java应用代码的健壮性的考虑,降低了编程门槛,减少不清楚底层机制的程序员无意破坏系统的概率。

    如今,Java的线程调度不提供抢占式中断,而采用协作式的中断。其实,协作式的中断,原理很简单,就是轮询某个表示中断的标记,我们在任何普通代码的中都可以实现。 例如下面的代码:

    volatile bool isInterrupted;

    //…

    while(!isInterrupted) {

        compute();

    }

    但是,上述的代码问题也很明显。当compute执行时间比较长时,中断无法及时被响应。另一方面,利用轮询检查标志变量的方式,想要中断wait和sleep等线程阻塞操作也束手无策。

    如果仍然利用上面的思路,要想让中断及时被响应,必须在虚拟机底层进行线程调度的对标记变量进行检查。是的,JVM中确实是这样做的。下面摘自java.lang.Thread的源代码:

        public static boolean interrupted() {
            return currentThread().isInterrupted(true);
        }

       //…

        private native boolean isInterrupted(boolean ClearInterrupted);

    可以发现,isInterrupted被声明为native方法,取决于JVM底层的实现。

    实际上,JVM内部确实为每个线程维护了一个中断标记。但应用程序不能直接访问这个中断变量,必须通过下面几个方法进行操作:
    public class Thread {
        //设置中断标记
        public void interrupt() { ... }  
        //获取中断标记的值
        public boolean isInterrupted() { ... }
        //清除中断标记,并返回上一次中断标记的值
        public static boolean interrupted() { ... }   
        ...
    }

    通常情况下,调用线程的interrupt方法,并不能立即引发中断,只是设置了JVM内部的中断标记。因此,通过检查中断标记,应用程序可以做一些特殊操作,也可以完全忽略中断。
    
    你可能想,如果JVM只提供了这种简陋的中断机制,那和应用程序自己定义中断变量并轮询的方法相比,基本也没有什么优势。
    
    JVM内部中断变量的主要优势,就是对于某些情况,提供了模拟自动“中断陷入”的机制。
    
    在执行涉及线程调度的阻塞调用时(例如wait、sleep和join),如果发生中断,被阻塞线程会“尽可能快的”抛出InterruptedException。因此,我们就可以用下面的代码框架来处理线程阻塞中断:
    try {
        //wait、sleep或join
    }
    catch(InterruptedException e) {
        //某些中断处理工作
    }
    所谓“尽可能快”,我猜测JVM就是在线程调度调度的间隙检查中断变量,速度取决于JVM的实现和硬件的性能。    

    然而,对于某些线程阻塞操作,JVM并不会自动抛出InterruptedException异常。例如,某些I/O操作和内部锁操作。对于这类操作,可以用其他方式模拟中断:
    1)java.io中的异步socket I/O
    读写socket的时候,InputStream和OutputStream的read和write方法会阻塞等待,但不会响应java中断。不过,调用Socket的close方法后,被阻塞线程会抛出SocketException异常。
    
    2)利用Selector实现的异步I/O
    如果线程被阻塞于Selector.select(在java.nio.channels中),调用wakeup方法会引起ClosedSelectorException异常。
    
    3)锁获取
    如果线程在等待获取一个内部锁,我们将无法中断它。但是,利用Lock类的lockInterruptibly方法,我们可以在等待锁的同时,提供中断能力。
    
    
    另外,在任务与线程分离的框架中,任务通常并不知道自身会被哪个线程调用,也就不知道调用线程处理中断的策略。所以,在任务设置了线程中断标记后,并不能确保任务会被取消。因此,有以下两条编程原则:
    1)除非你知道线程的中断策略,否则不应该中断它。
        这条原则告诉我们,不应该直接调用Executer之类框架中线程的interrupt方法,应该利用诸如Future.cancel的方法来取消任务。
    
    2)任务代码不该猜测中断对执行线程的含义。
        这条原则告诉我们,一般代码遇在到InterruptedException异常时,不应该将其捕获后“吞掉”,而应该继续向上层代码抛出。
        
    总之,Java中的非抢占式中断机制,要求我们必须改变传统的抢占式中断思路,在理解其本质的基础上,采用相应的原则和模式来编程。

Java线程中断的本质和编程原则的更多相关文章

  1. Java线程中断的本质深入理解(转)

    一.Java中断的现象 首先,看看Thread类里的几个方法: public static boolean interrupted 测试当前线程是否已经中断.线程的中断状态 由该方法清除.换句话说,如 ...

  2. Java线程中断的本质深入理解

    Java的中断是一种协作机制.也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己. 一.Java中断的现象 首先,看看Thread类里的 ...

  3. 一文搞懂 Java 线程中断

    在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程的方法吗?答案是肯定的,它就是我们今天要分 ...

  4. Java线程的优先级设置遵循什么原则?

    Java线程的优先级设置遵从下述原则: (1) 线程创建时,子线程继承父线程的优先级 (2) 线程创建后,可在程序中通过调用setPriority( )方法改变线程的优先级 (3) 线程的优先级是1~ ...

  5. Java线程中断理解(interrupte)

    Java线程之中,一个线程的生命周期分为:初始.就绪.运行.阻塞以及结束.当然,其中也可以有四种状态,初始.就绪.运行以及结束. 一般而言,可能有三种原因引起阻塞:等待阻塞.同步阻塞以及其他阻塞(睡眠 ...

  6. java线程中断[interrupt()函数] (转载)

    一个正常的线程中断: 从运行到真正的结束,应该有三个阶段: 正常运行. 处理结束前的工作,也就是准备结束. 结束退出. Java曾经提供过抢占式限制中断,但问题多多,例如的Thread.stop.另一 ...

  7. java线程中断和终止线程运行

    ava中启动一个线程很容易,通常情况下我们都是等到任务运行结束后让线程自行停止.但有时需要在任务正在运行时取消他们,使得线程快速结束.对此Java并没有提供任何机制.但是我们可以通过Java提供的线程 ...

  8. java线程中断的办法

    目录 中断线程相关的方法 中断线程 for循环标记退出 阻塞的退出线程 使用stop()方法停止线程 中断线程相关的方法 中断线程有一些相应的方法,这里列出来一下. 注意,如果是Thread.meth ...

  9. Java 线程安全问题的本质

    原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94 目录: 线程安全问题的本质 理解CPU JVM虚拟机类比于操作系统 重排序 汇总 一些解释 ...

  10. java线程中断2

    一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果. 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了.那么不 ...

随机推荐

  1. WebRTC 简单入门与实践

    一.前言 WebRTC 技术已经广泛在各个行业及场景中被应用,但对多数开发者来说,实时音视频及相关技术却是比较不常接触到的. 做为一名 Web 开发者,WebRTC 这块的概念着实花了不少时间才搞明白 ...

  2. 【转】一种Vue应用程序错误/异常处理机制

    在前端应用程序中,最常见的错误/异常类型可能包括以下几种: 语法错误:使用了一些错误的语法 运行时错误:由于执行期间的非法操作导致的 逻辑错误:由于程序逻辑错误 Http 错误:API 返回的错误 有 ...

  3. Azure – 对比 AWS Research Report

    前言 最近有机会接触了一下 AWS, 在对比完之后决定继续用 Azure, 这里小小记入一下. VM & SQL Server Azure 和 AWS 都可以选择 2 in 1, 或者 Web ...

  4. 浏览器原生组件 shadow DOM(已完成)

    https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_shadow_DOM Web components 的一个重要属性是 ...

  5. 应聘软件测试 HR 会问到哪些问题?收藏这一篇就够了!

    1.你还有收到其他offer吗? 其实hr问你offer情况,是对你感兴趣,想要进一步了解你,看下你的市场竞争力. 但注意不要太坦诚的说:我还没有offer或者收到两个offer还想对比对比:也不要撒 ...

  6. 谷歌浏览器页面乱码问题在浏览器端解决,charset下载安装;

    一   下载插件(百度网盘) 链接:https://pan.baidu.com/s/1o9Zuo2m 密码:rrcz 二    将下载好的插件拖到谷歌浏览器中 三    如果谷歌浏览器右下角出现如下图 ...

  7. Oracle 23c 新特性实操体验优质文章汇总 | 有奖征文进行中欢迎参与

    继4月3日甲骨文宣布推出免费开发者版 Oracle Database 23c后,墨天轮社区发起 "Oracle 23c 免费开发者版特性体验"有奖征文活动,邀请大家分享Oracle ...

  8. 如何解决使用 router.push 跳转路由第二次之后页面就不会刷新了

    router.push({ name:"monitor", query:{ deviceid:"1676156672197922816", // 设备 ID i ...

  9. vue3自动导入 api ,不需要多次导入 api 了

    安装插件   npm i -D unplugin-auto-import 配置 vite.config.js export default defineConfig({ plugins: [ vue( ...

  10. 63.CDN优化

    虽然CDN引入组件库可以优化项目,减轻服务器负载,但是在真实的项目开发中不推荐使用CDN : 因为: 1. 使用第三方服务器不稳定 2. 需要后端配置 3. 要知道组件库的全局变量名