1 什么是中断机制
- 一个线程不应该由其他线程中断或停止,而应该由线程自行停止——命运掌握在自己手里;
- 所以Thread.stop、Thread.suspend、Thread.resume方法都已过时;
- Java中无法立即停止一个线程;
- Java提供了一种用于停止线程的协商机制——中断,也即中断标识协商机制;
- 中断过程需要程序员自己实现——用线程的interrupt方法将对象的中断标识设置为true——只是温馨提示,不是强制终止;
2 中断的相关API方法之三大方法说明
- void interrupt():
- 中断此线程;
- 只是设置中断标志位,发起一个协商;
- static boolean interrupted():
- 测试当前线程是否已被中断;
- 静态方法;
- 方法做了两件事:
- 返回当前线程的中断状态(true/false),测试当前的线程是否已被中断;
- 将当前线程的中断状态清零并重新设为false,清除线程的中断状态;
- boolean isInterrupted():
- 判断当前线程是否被中断(通过检查中断标志位);
- 和第一个方法配合使用;
3 如何停止中断运行中的线程——代码示例
3.1 通过关键字volatile变量实现
import java.util.concurrent.TimeUnit;
public class InterruptDemo
{
private static volatile boolean isStop = false;
public static void main(String[] args)
{
new Thread(() -> {
while(true)
{
// 需要退出了 or 需要中断了
if(isStop)
{
System.out.println(Thread.currentThread().getName()+"线程------isStop = true,自己退出了");
break;
}
System.out.println("-------hello interrupt");
}
},"t1").start();
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); }
isStop = true;
}
3.2 通过AtomicBoolean类实现
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class StopThreadDemo
{
// init
private final static AtomicBoolean atomicBoolean = new AtomicBoolean(true);
public static void main(String[] args)
{
new Thread(() -> {
while(true)){
if(atomicBoolean.get()){
System.out.println(
Thread.currentThread().getName() + "\t atomicBoolean 被修改为true 线程停止"
);
break;
}
System.out.println("t1-----hello");
}
}, "t1").start();
// 小睡一会
try {
TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {
e.printStackTrace(); }
// 另一个线程
new Thread(() -> {
atomicBoolean.set(true);
},"t2").start();
}
}
}
3.3 通过Thread类自带的中断API方法实现
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class InterruptTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while(true) {
// 判断中断
if(Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + "\t 中断");
break;
}
System.out.println("----t1 interrupt");
}
},"t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(() -> {
// 中断
t1.interrupt();
},"t2").start();
}
}
4 中断方法底层源码分析
4.1 interrupt()方法——无返回值
- interrupt方法作用:中断此线程;
- 仅仅是将中断标识位从false设置为true;
- 并不是立刻停止线程;
- 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态 (不是立刻退出线程),并抛出一个InterruptedException异常。;
- 中断不活动的线程不会产生任何影响;
方法:
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
// 实际调用interrupt0()
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
调用底层native方法:
private native void interrupt0();
4.2 isInterrupted()方法
public boolean isInterrupted() {
return isInterrupted(false);
}
底层还是调用native方法:
// Tests if some Thread has been interrupted.
// The interrupted state is reset or not based on the value of ClearInterrupted that is passed.
private native boolean isInterrupted(boolean ClearInterrupted);
5 面试题
5.1 案例1
当前线程的中断标识为true,是不是立刻停止?
——不是立刻停止 需要线程自己配合;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class InterruptTest {
public static void main(String[] args) throws InterruptedException
{
Thread t1 = new Thread(() -> {
for (int i=0;i<300;i++) {
System.out.println("-------"+i);
}
System.out.println("after t1.interrupt()--第2次---: "+Thread.currentThread().isInterrupted());
},"t1");
t1.start();
// 默认是false
System.out.println("before t1.interrupt()----: "+t1.isInterrupted());
//实例方法interrupt()仅仅是设置线程的中断状态位设置为true,不会停止线程
t1.interrupt();
//活动状态,t1线程还在执行中
try {
TimeUnit.MILLISECONDS.sleep(3); } catch (InterruptedException e) {
e.printStackTrace(); }
// 这里已经是true 但是程序没有立刻中断
System.out.println("after t1.interrupt()--第1次---: "+t1.isInterrupted());
//非活动状态,t1线程不在执行中,已经结束执行了。
try {
TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException e) {
e.printStackTrace(); }
System.out.println("after t1.interrupt()--第3次---: "+t1.isInterrupted());
}
}
5.2 案例2
- 问题:线程t1处理阻塞状态(sleep),线程t2调用了t1的interrupt方法,抛出异常后,开始死循环,程序不会结束;
- 解决方法:在catch里再一次调用Thread.currentThread().interrupt();
为什么?
- 1 在没有调用t1.interrupt()之前,中断标志位默认为false;
- 2 t2发出中断协商,t1.interrupt(),中断标志位被设置为true;
- 正常情况下,中断标志位为true,程序停止;
- 异常情况,t1正在sleep——t1的中断状态将会被清除(从true变为false),并抛出InterrptedException——导致无限循环;
- 3 在catch块里再次设置:Thread.currentThread().interrupt(); ——防止死循环;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class InterruptTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while(true) {
// 中断
if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName() + "\t 被设置为true");
break;
}
// 阻塞 java.lang.InterruptedException: sleep interrupted
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// 再调用一次 就不会无限循环了
// Thread.currentThread().interrupt();
e.printStackTrace();
}
System.out.println("----hello");
}
}, "t1");
t1.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() ->
t1.interrupt(),"t2").start();
}
}
5.3 案例3
——谈谈对静态方法Thread.interrupted()的理解?
判断线程是否被中断并清除当前中断状态:
● 1 返回当前线程的中断状态,测试当前线程是否已经被中断;
● 2 将当前线程的中断状态清零,重新设置为false,则第二次调用将返回false,两次连续调用结果可能不一样;
public class InterruptTest {
public static void main(String[] args) {
// main线程 连续两次调用
System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
System.out.println("-----------1");
// 设置中断
Thread.currentThread().interrupt();
System.out.println("-----------2");
// 连续两次调用
System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
// main false
// main false
// -----------1
// -----------2
// main true
// main false
}
}
两个底层代码:
两者底层都是调用isInterrupted方法,一个默认为true,一个默认为false;
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}