引言

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. Django与多个数据库交互

    定义数据库 在Django中使用多个数据库的第一步是告诉Django您将要使用的数据库服务器. 数据库可以有您选择的任何别名.但是,别名 default 有着特殊的意义.Django使用别名为 def ...

  2. LeetCode(123) Best Time to Buy and Sell Stock III

    题目 Say you have an array for which the ith element is the price of a given stock on day i. Design an ...

  3. TextView设置缩略显示

    1.代码设置 textview.setSingleLine(); textview.setEllipsiz(TextUtils.TruncateAt.valueOf("END")) ...

  4. Win磁盘MBR转换为GUID

    title: Win磁盘MBR转换为GUID date: 2018-09-02 11:52:32 updated: tags: [windows,记录,折腾] description: keyword ...

  5. PHP中文网 学习阶段规划

    1.第一阶段: 前端基础 前端基础课程大纲 教学内容 教学重点 1.HTML5 HTML简介.HTML标签详解.字符编码的奥秘.Html5新特性与常用标签 2.CSS3 CSS简介.CSS的引入方式. ...

  6. 各浏览器对 window.open() 的支持

    原文地址

  7. 大数据学习——scala函数与方法

    package com /** * Created by Administrator on 2019/4/8. */ object TestMap { def ttt(f: Int => Int ...

  8. Python学习-day4

    学习装饰器,首先听haifeng老师讲解了一下准备知识. 1.函数即变量 2.高阶函数+嵌套函数==>装饰器 装饰器的作用是在,1)不改变源代码,2)不改变原函数的调用方式的前提下为函数增加新的 ...

  9. SDOJ 3696 Tree

    描述 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. 输入 第一行V,E,need分别表示点数,边数和需要的白色边数. 接下来E行 每行 ...

  10. 【bzoj2225】[Spoj 2371]Another Longest Increasing CDQ分治+树状数组

    题目描述 给定N个数对(xi, yi),求最长上升子序列的长度.上升序列定义为{(xi, yi)}满足对i<j有xi<xj且yi<yj. 样例输入 8 1 3 3 2 1 1 4 5 ...