引言

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. Fiddler证书安装不成功

    Fiddler 抓包https配置 提示creation of the root certificate was not successful 证书安装不成功 原文链接 在使用Fiddler抓包时,我 ...

  2. UVa 1366 DP Martian Mining

    网上的题解几乎都是一样的: d(i, j, 0)表示前i行前j列,第(i, j)个格子向左运输能得到的最大值. d(i, j, 1)是第(i, j)个格子向上运输能得到的最大值. 但是有一个很关键的问 ...

  3. Linux权限和指令的关系

    1.让用户能进入某目录称为”可工作目录“的基本权限为何: 可使用的指令:例如cd等变换工作目录的指令: 目录所需权限:用户对这个目录至少需要具有x的权限 额外需求:如果用户想要在这个目录内利用ls查阅 ...

  4. 03011_HttpServletRequest

    1.HttpServletRequest概述 (1)我们在创建Servlet时会覆盖service()方法,或doGet()/doPost(),这些方法都有两个参数,一个为代表请求的request和代 ...

  5. luogu3369 【模板】普通平衡树(Treap/SBT) treap splay

    treap做法,参考hzwer的博客 #include <iostream> #include <cstdlib> #include <cstdio> using ...

  6. Apache不能启动: Unable to open logs

    日志名称:          Application来源:            Apache Service日期:            2014/3/12 14:43:21事件 ID:       ...

  7. Selenium WebDriver- 使用显示等待,判断搜狗的输入框是否显示,按钮是否可点击,然后在输入光荣之路搜索词,然后在点击搜索。

    #encoding=utf-8 from selenium import webdriver import time from selenium.webdriver.common.by import ...

  8. 突然想看单纯形 BZOJ3265 志愿者招募加强版

    本来的版本是可以差分之后建图利用网络流,这个题是板子题,就当存个板子,嘻嘻嘻 讲解可以到卿学姐的算法讲堂 https://www.bilibili.com/video/av7847726?from=s ...

  9. Convolutional Networks for Image Semantic Segmentation

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/52857657 把前段时间自己整理的一个 ...

  10. .net SignalR 聊天室

    代码地址:https://gitee.com/srnsrn/netSignalr.git 运行项目打开多个页面 私密聊天