引言

Java没有提供任何机制来安全地终止线程,但提供了中断机制,即thread.interrupt()方法。线程中断是一种协作式的机制,并不是说调用了中断方法之后目标线程一定会立即中断,而是发送了一个中断请求给目标线程,目标线程会自行在某个取消点中断自己。这种设定很有必要,因为如果不论线程执行到何种情况都立即响应中断的话,很容易造成某些对象状态不一致的情况出现。

正文

一、中断相关的方法介绍

涉及到中断的线程基础方法有三个:interrupt()、isInterrupted()、interrupted(),它们都位于Thread类下。Thread类下还有一个

interrupt()方法:对目标线程发送中断请求,看其源码会发现最终是调用了一个本地方法实现的线程中断;

interrupted()方法:返回目标线程是否中断的布尔值(通过本地方法实现),且返回后会重置中断状态为未中断;

isInterrupted()方法:该方法返回的是线程中断与否的布尔值(通过本地方法实现),不会重置中断状态;

二、线程中断

线程中断可以按中断时线程状态分为两类,一类是运行时线程的中断,一类是阻塞或等待线程的中断。有中断时,运行时的线程会在某个取消点中断执行,而处于阻塞或者等待状态的线程大多会立即响应中断,比如上一篇文章中提到的join、sleep等方法,这些方法在抛出中断异常的错误后,会重置线程中断状态为未中断。但注意,获取独占锁的阻塞状态与BIO的阻塞状态不会响应中断。而在JUC包中有在加锁阻塞的过程中响应中断的方法,比如lockInterruptibly()。

下面从三个问题来讲述线程中断

1、线程中断的目的是什么?

为什么要进行线程中断?有时是由于对于某种特定情况,我们知道当前线程无需继续执行下去,此时可以中断此线程;有时是遇到某些异常,需要中断线程。具体什么目的,还要看具体场景,但线程中断的需求已经摆在那里,肯定需要。

2、要如何处理线程中断?

通常的处理方式有两种,如果是业务层面的代码,则只需要做好中断线程之后的业务逻辑处理即可,而如果是偏底层功能的线程中断,则尽量将中断异常抛出(或者在catch中重新调用interrupt()来中断线程),以告知上层方法本线程的中断经历。

3、JUC中对中断的处理举例

JUC中ReentrantLock常用的加锁方法是lock(),还有一个响应中断的加锁方法lockInterruptibly()

lock()方法中的acquire(int arg)方法如下所示:

 public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
5 }

在acquireQueued中会对线程的中断状态做判断,如果中断了则返回true,进入selfInterrupt()方法,恢复线程的中断状态。但注意此处是在获取到锁之后再响应中断,在获取到锁之前不会做出响应。

 static void selfInterrupt() {
Thread.currentThread().interrupt();
}

而看lockInterruptibly()方法:

 public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
} public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}

它会先查看中断状态,再获取锁。而如果在获取锁的过程中中断过,则会在doAcquireInterruptibly方法中抛出中断异常。

下面是我在本地模拟的lock阻塞中断:

 public class ReentrantLockDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("main start");
Thread thread1 = new Thread(new LockThreadDemo());
Thread thread2 = new Thread(new LockThreadDemo());
thread1.start();
Thread.sleep(1000); // 确保thread1获取到了锁
thread2.start(); // 此时thread2处于获取锁的阻塞状态
thread2.interrupt();
System.out.println("main end");
}
} class LockThreadDemo implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "runnable run");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "开始睡眠");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "睡了5秒");
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "runnable exception:" + e);
} finally {
lock.unlock();
}
System.out.println(Thread.currentThread().getName() + " over");
}
}

执行结果为:

main start
Thread-0runnable run
Thread-0开始睡眠
main end
Thread-1runnable run
Thread-0睡了5秒
Thread-0 over
Thread-1开始睡眠
Thread-1runnable exception:java.lang.InterruptedException: sleep interrupted
Thread-1 over

可以看到中断了并没有对获取锁产生影响,最后是sleep方法响应的中断。

下面是我在本地模拟的lockInterruptibly()阻塞中断:

 public class ReentrantLockInterruptableDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("main start");
Thread thread1 = new Thread(new LockThreadInterruptableDemo());
Thread thread2 = new Thread(new LockThreadInterruptableDemo());
thread1.start();
Thread.sleep(1000); // 确保thread1获取到了锁
thread2.start(); // 此时thread2处于获取锁的阻塞状态
thread2.interrupt();
System.out.println("main end");
}
} class LockThreadInterruptableDemo implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "runnable run");
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "开始睡眠");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "睡了5秒");
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "runnable exception:" + e);
} finally {
try {
lock.unlock();
} catch (IllegalMonitorStateException e) {
System.out.println("因线程" + Thread.currentThread().getName() + "提前中断导致未获取到锁");
}
}
System.out.println(Thread.currentThread().getName() + " over");
}
}

结果为:

main start
Thread-0runnable run
Thread-0开始睡眠
main end
Thread-1runnable run
Thread-1runnable exception:java.lang.InterruptedException
因线程Thread-1提前中断导致未获取到锁
Thread-1 over
Thread-0睡了5秒
Thread-0 over

结束语

对于线程中断的处理比较常见,尤其是涉及到多线程的框架、组件中。而能否处理好线程中断的各种情况,则体现出一个程序员对多线程掌握的熟练情况。每天进步一点,日拱一卒,加油!

Java线程的中断的更多相关文章

  1. Java线程的中断(Interruption)

    任务和线程的启动很容易.在大多数时候,我们都会让它们运行直到结束,或者让它们自行停止.然而,有时候我们希望提前结束任务或线程,或许是因为用户取消了操作,或者应用程序需要被快速关闭. 要使任务和线程能安 ...

  2. 理解java线程的中断(interrupt)

    一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果比如会带着自己所持有的锁而永远的休眠,迟迟不归还锁等. 所以你看到Thread.suspend, Threa ...

  3. java 线程的中断

    Example12_6.java public class Example12_6 { public static void main(String args[]) { ClassRoom room6 ...

  4. Java线程机制学习

    前面的文章中总结过Java中用来解决共享资源竞争导致线程不安全的几种常用方式: synchronized: ReentrantLock: ThreadLocal: 这些都是在简单介绍了基本用法的基础上 ...

  5. Java线程状态、线程start方法源码、多线程、Java线程池、如何停止一个线程

    下面将依次介绍: 1. 线程状态.Java线程状态和线程池状态 2. start方法源码 3. 什么是线程池? 4. 线程池的工作原理和使用线程池的好处 5. ThreadPoolExecutor中的 ...

  6. java并发:中断一个正在运行的线程

    要使任务和线程能安全可靠地停止,并不是一件很容易的事情,java没有提供任何机制来安全地终止线程,那么我们该怎么办呢? 下面我们先来了解一下java中的中断机制: java中断机制是一种协作机制,也就 ...

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

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

  8. lesson6:java线程中断

    正常的情况下,业务系统都不会去中断它的线程,但是由于一些特殊情况的发生,线程已经不能正常结束了,并且此类线程已经影响到业务系统提供服务的能力,如果系统设计的健壮,便会通过监控线程去主动的中断此类线程. ...

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

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

随机推荐

  1. 【nginx】nginx.sh nginx 安装脚本

    #! /bin/shcd /usr/local/srcwget http://nginx.org/download/nginx-1.10.1.tar.gzecho 'download success' ...

  2. 17.Yii2.0框架模型添加记录

    目录 新建控制器 HomeController.php 新建model Article.php 新建控制器 HomeController.php D:\xampp\htdocs\yii\control ...

  3. IOC容器和Bean的配置实例

    实验1: <!--实验1:通过IOC容器创建对象,并为属性赋值 --> <!-- 需要由IOC容器创建对象的全类名 --> <!-- 为了便于从IOC容器中获取book对 ...

  4. nrf52810学习笔记——二

    nrf52810为nordic支持蓝牙 5.0性价比最高的一款芯片,不过这个芯片的rom不得不吐槽下,只有192KB,不知道为什么定了个192,而不是大家所熟悉的256KB,估计价格是个原因吧,15. ...

  5. Nordic Collegiate Programming Contest 2015​ E. Entertainment Box

    Ada, Bertrand and Charles often argue over which TV shows to watch, and to avoid some of their fight ...

  6. goalng导出excel(csv格式)

    最近项目中有个小需求,需要将查询结果导出到excel.之间前java比较容易,使用POI很容易就能实现,查了下golang的文档,发现golang下边并没有导出excel的包,但是却有一个encodi ...

  7. css 阴影使用

    文本阴影 p{ text-shadow: 5px 5px 5px #FF0000; } text-shadow: h-shadow v-shadow blur color; text-shadow: ...

  8. Java-一个数组中的元素复制到另一个数组

    public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length).其中五个参数分别表示为: ...

  9. 0014.Linux环境搭建 Python环境搭建

    -安装Linux-- 找了了老男孩19期的运维班安装视频,尼玛真心不想看书,文字枯燥的要死,还不如直接看视频进行安装... 可怜了我的C盘只有1GB了...绝对不能安装在C盘...那就安装在E盘吧,足 ...

  10. SVM用于线性回归

    SVM用于线性回归 方法分析 在样本数据集()中,不是简单的离散值,而是连续值.如在线性回归中,预测房价.与线性回归类型,目标函数是正则平方误差函数: 在SVM回归算法中,目的是训练出超平面,采用作为 ...