深入Java线程中断的本质与编程原则的概述

在历史上,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中的非抢占式中断机制,要求我们必须改变传统的抢占式中断思路,在理解其本质的基础上,采用相应的原则和模式来编程。

(0)

相关推荐

  • 深入Java线程中断的本质与编程原则的概述

    在历史上,Java试图提供过抢占式限制中断,但问题多多,例如前文介绍的已被废弃的Thread.stop.Thread.suspend和 Thread.resume等.另一方面,出于Java应用代码的健壮性的考虑,降低了编程门槛,减少不清楚底层机制的程序员无意破坏系统的概率. 如今,Java的线程调度不提供抢占式中断,而采用协作式的中断.其实,协作式的中断,原理很简单,就是轮询某个表示中断的标记,我们在任何普通代码的中都可以实现. 例如下面的代码:    volatile bool isInter

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

    一.Java中断的现象 首先,看看Thread类里的几个方法: public static boolean interrupted 测试当前线程是否已经中断.线程的中断状态 由该方法清除.换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外). public boolean isInterrupted() 测试线程是否已经中断.线程的中断状态 不受该方法的影响. public void in

  • 详解Java 线程中断

    一.前言 大家肯定都使用过 Java 线程开发(Thread / Runnable),启动一个线程的做法通常是: new Thread(new Runnable( @Override public void run() { // todo sth... } )).start(); 然而线程退出,大家是如何做的呢?一般做法可能不外乎以下两种: 设置一个标志位:true / false 来退出: 强制退出:thread.stop:(我相信,现在应该没人会使用这种方式了,因为JDK也很早就废弃了该方法

  • 详解Java线程中断知识点

    下面的这断代码大家应该再熟悉不过了,线程休眠需要捕获或者抛出线程中断异常,也就是你在睡觉的时候突然有个人冲进来把你吵醒了. try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } 此时线程被打断后,代码会继续运行或者抛出异常结束运行,这并不是我们需要的中断线程的作用. 到底是什么是线程中断? 线程中断即线程运行过程中被其他线程给打断了,它与 stop 最大的区别是:stop 是由系统强

  • Java线程中断interrupt的常用方法

    目录 前言 示例说明 注意事项 Object#wait 和 Thread.sleep 差异在哪里 总结 前言 这里主要探讨中断常用的三个方法: interrupt().在一个线程中调用需要中断现成的interrupt()方法,会对该线程发出信号,将中断状态标志为true isInterrupted().判断当前线程的中断状态. interrupted().将线程的中断状态恢复. 主要使用的阻塞三个方法: Object#wait.放弃锁+等待+重新获取锁 Thread#join.[协作]等待某个线

  • 浅析java线程中断的办法

    中断线程相关的方法 中断线程有一些相应的方法,这里列出来一下. 注意,如果是Thread.method(),则代表是静态方法.如果是thread.method()则代表着是类方法 void thread.stop() 这个方法能中断正在运行的线程,但是已经不推荐使用了,在将来的版本或许弃用,因为强行中断运行中的线程,是不安全的. void thread.interrupt() 如果正在运行wait(),sleep(),join()这三个方法阻塞了线程,那么将会使得线程抛出InterruptedE

  • Java并发编程之线程中断

    目录 线程中断: void interrupted()方法:中断线程,例如,当线程A运行时,线程B可以调用线程A的interrupted()方法来设置线程的中断标志为true并立即返回.设置标志仅仅是为了设置标志,线程A实际并没有被中断,它会继续往下执行,如果线程A因为调用了wait()方法,join()方法或者sleep()方法而引起的阻塞挂起,这时候若线程B调用线程A的interrupted()方法,线程A回调用这些方法的地方会抛出InterruptedException异常而返回. boo

  • 聊聊Java 中的线程中断

    Java如何实现线程中断? 通过调用Thread类的实例方法interrupt.如下: Thread thread = new Thread(){ @Override public void run() { if(isInterrupted()){ System.out.println("interrupt"); } } }; thread.start(); thread.interrupt(); 线程中断后线程会立即停止执行吗? NO. 而如果线程未阻塞,或未关心中断状态,则线程会正

  • Java并发编程示例(四):可控的线程中断

    在上一节"线程中断"中,我们讲解了如何中断一个正在执行的线程以及为了中断线程,我们必须对Thread动点什么手脚.一般情况下,我们可以使用上一节介绍的中断机制.但是,如果线程实现了一个分配到多个方法中的复杂算法,或者方法调用中有一个递归调用,我们应该使用更好的方式来控制线程的中断.为此,Java提供了InterruptedException异常.当检测到中断请求时,可以抛出此异常,并且在run()方法中捕获. 在本节,我们将使用一个线程查找指定目录及其子目录下文件来演示通过使用Inte

  • 详解Java线程编程中的volatile关键字的作用

    1.volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 2)禁止进行指令重排序. 先看一段代码,假如线程1先执行,线程2后执行: //线程1 boolean stop = false; while(!stop){ doSomething(); } //线程2 stop = true; 这段代码是很典型

随机推荐