开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
学习MOOC视频记录的笔记
1.原理介绍
原理介绍:使用
interrupt来通知,而不是强制
合作机制,让一个线程来通知另一个线程,你该中断了,被中断的线程本身有决定权,停不停止,什么时候停止。因为没有办法做到强行停止,只能是一种建议。将停止线程的权利交给了被停止线程本身。被停止线程本身更清楚如何去执行后续的清理工作。
2.最佳实践:如何正确停止线程
2.1 通常线程会在什么情况下停止
run方法的所有代码都执行完毕- 有异常出现,并且方法中没有捕获
2.2 正确的停止方法:interrupt
2.2.1 通常线程会在什么情况下停止【普通情况】
使用thread.interrupt();来停止该线程的同时还必须使用 Thread.currentThread().isInterrupted() 来实时检测线程是否被中断了
/**
* run方法内没有sleep或wait方法时,停止线程
*/
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
// 线程没有被中断并且num还在范围之内
while(!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
if(num % 10000 == 0) {
System.out.println(num + "是10000的倍数");
}
num++;
}
System.out.println("任务运行结束了");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
2.2.2 线程可能被阻塞
/**
* 带有sleep的中断线程的写法
*/
public class RightWayStopThreadWithSleep {
public static void main(String[] args) throws InterruptedException {
// 在等待期间线程被中断了
Runnable runnable = () -> {
int num = 0;
try {
while(num <= 300 && !Thread.currentThread().isInterrupted()) {
if(num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
}
// 线程睡眠1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
// 在sleep的过程中被打断了
thread.interrupt();
}
}
输出:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:22)
at java.lang.Thread.run(Thread.java:748)
子线程以抛出异常的方式响应中断。
线程进入阻塞状态中仍然被中断。
2.2.3 如果线程在每次迭代后都阻塞
for 循环中每个位置都有
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
// 在等待期间线程被中断了
Runnable runnable = () -> {
int num = 0;
try {
while(num <= 10000 && !Thread.currentThread().isInterrupted()) {
if(num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
// 线程睡眠1s,每次循环都阻塞
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
// 在sleep的过程中被打断了
thread.interrupt();
}
}
输出:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:21)
at java.lang.Thread.run(Thread.java:748)
如果不判断异常 !Thread.currentThread().isInterrupted()也正常抛出异常了,因此如果阻塞在循环中,不需要进行判断。
/**
* 如果在执行过程中,每次循环都会调用sleep或wait等方法,那么那么不需要每次迭代都检查是否已中断
*/
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
// 在等待期间线程被中断了
Runnable runnable = () -> {
int num = 0;
try {
while(num <= 10000) {
if(num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
// 线程睡眠1s,每次循环都阻塞
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
// 在sleep的过程中被打断了
thread.interrupt();
}
}
输出:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:21)
at java.lang.Thread.run(Thread.java:748)
仍然存在的问题:try-catch 在里面的时候,这段代码执行完毕会进入下一个循环。
/**
* 如果while里面放try/catch,会导致中断失效
*/
public class CantInterrupt {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while(num <= 10000) {
if(num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
thread.interrupt();
}
}
输出如下:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.CantInterrupt.lambda$main$0(CantInterrupt.java:16)
at java.lang.Thread.run(Thread.java:748)
500是100的倍数
600是100的倍数
700是100的倍数
800是100的倍数
再增加了检查中断的代码之后:
/**
* 如果while里面放try/catch,会导致中断失效
*/
public class CantInterrupt {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while(num <= 10000 && !Thread.currentThread().isInterrupted()) {
if(num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
thread.interrupt();
}
}
结果还是和上面一样的。
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.CantInterrupt.lambda$main$0(CantInterrupt.java:17)
at java.lang.Thread.run(Thread.java:748)
400是100的倍数
500是100的倍数
600是100的倍数
一旦捕获到了异常(一旦响应了中断),标记位会被清除,因此检测不到。
实际开发中的两种最佳实践
- 优先选择:传递中断 【在方法的签名上抛出异常】
- 不想或无法传递:恢复中断 【人为手动恢复中断,在子方法的
catch中抛出Thread.currentThread.interrupt();】 - 不应屏蔽中断
/**
* 最佳实践:catch了InterruptedException之后的优先选择:在方法签名中抛出异常
* 那么在run()方法中就会强制try/catch
*/
public class RightWayStopThreadInProd implements Runnable {
// 较高层次
@Override
public void run() {
while(true) {
System.out.println("go");
throwInMethod();
}
}
// 这种方法是将异常处理在很低级的层次
private void throwInMethod() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
运行结果如下:
go
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd.throwInMethod(RightWayStopThreadInProd.java:23)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd.run(RightWayStopThreadInProd.java:16)
at java.lang.Thread.run(Thread.java:748)
go
go
go
go
go
go
因为异常是在方法 throwInMethod 中被处理的,而 run() 方法一直是一个死循环,所以会继续执行。
即使加上了条件判断也无法得到正确的信息【sleep过程中被中断会清除标记位,所以这个标记位传不到 while 循环条件中,线程 thread 在休眠过程中遇到了中断,只是将这个异常打印了出来,没有做额外的处理,直接吞了,而没有上传给 run 函数去处理,应该做的是将这个异常传递上去,不应该直接吞掉】
public void run() {
while(true && !Thread.currentThread().isInterrupted()) {
System.out.println("go");
throwInMethod();
}
}
run()方法中只能用 try-catch 进行处理,无法继续向上抛出异常
/**
* run()方法抛出checked Exception,只能用try/catch
*/
public class RunThrowException {
// 普通方法直接在方法签名中抛出异常
public void aVoid() throws Exception {
throw new Exception();
}
// run() 方法,错误,无法抛出异常
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() throws Exception {
throw new Exception();
}
});
}
}
出现这种情况的原因需要回归到面向对象知识,因为 run() 方法是重写的方法,而顶层方法中没有进行任何异常的抛出,而仅仅是对其进行重写,不能改变其对于异常的定义,所以不允许抛出异常。【因此其抛出异常的范围不能大于重写之前的方法】
public abstract void run();
修改之后的完整代码如下,即正确的做法:
/**
* 最佳实践:catch了InterruptedException之后的优先选择:
* 在方法签名中抛出异常,那么在run()方法中就会强制try/catch
*/
public class RightWayStopThreadInProd implements Runnable {
// 较高层次
@Override
public void run() {
while(true && !Thread.currentThread().isInterrupted()) {
System.out.println("go");
try {
throwInMethod();
} catch (InterruptedException e) {
// 保存日志、停止程序
System.out.println("保存日志");
e.printStackTrace();
}
}
}
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
运行情况如下:
go
保存日志
go
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd.throwInMethod(RightWayStopThreadInProd.java:28)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd.run(RightWayStopThreadInProd.java:17)
at java.lang.Thread.run(Thread.java:748)
go
go
经验总结:如果编写一个提供给
run方法调用的函数,如果里面要抛出异常,那么最好把这个异常向上抛出,由顶层的run方法来进行处理。在catch中进一步完善。
不想或无法传递:恢复中断
/**
* 最佳实践2:在catch子语句中调用Thread.currentThread().interrupt()
* 来恢复设置中断状态,回到刚才RightWayStopThreadInProd2补上中断,让它跳出
*/
public class RightWayStopThreadInProd2 implements Runnable {
// 较高层次
@Override
public void run() {
while (true) {
// 可以获取到reInterrupt()方法中抛出的中断信息
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted,程序运行结束");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// 获取到中断信息并重新抛出来
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
程序运行结果如下:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd2.reInterrupt(RightWayStopThreadInProd2.java:26)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd2.run(RightWayStopThreadInProd2.java:20)
at java.lang.Thread.run(Thread.java:748)
Interrupted,程序运行结束
Process finished with exit code 0
如果注释掉 Thread.currentThread().interrupt(); 不向上抛出异常:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd2.reInterrupt(RightWayStopThreadInProd2.java:26)
at threadcoreknowledge.stopthread.RightWayStopThreadInProd2.run(RightWayStopThreadInProd2.java:20)
at java.lang.Thread.run(Thread.java:748)
此时程序还在继续执行 while(true) 里面的代码,因为这个中断的异常直接被吞了。
响应中断的方法总结列表
Object.wait()/ wait(long)/ wait(long, int)Thread.sleep(long)/ sleep(long,int)Thread.join()/ join(long)/ join(long, int)java.util.concurrent.BlockingQueue.take()/ put(E)java.util.concurrent.locks.Lock.lockInterruptibly()java.util.concurrent.CountDownLatch.await()java.util.concurrent.CyclicBarrier.await()java.util.concurrent.Exchanger.exchange(V)java.nio.channels.InterruptibleChannel相关方法java.nio.channels.Selector的相关方法
以上方法中可以直接使用 interrupt 方法来中断线程。
为什么要使用 interrupt 来中断线程。
因为调用的时候是使用 t.interrupt() 发出一个信号来中断一个线程,t 就是被中断的线程,即被中断的线程有主动终止的权利。
Error 是系统内部的错误,如内存不足等,无法通过代码层面将Error catch
Exception 分为两类:
RuntimeException非受检异常,一旦发生,一定是程序员的问题 如空指针,数组越界等,【编译器无法提前预测的】- 非
RuntimeException受检异常 如打开一个不存在的文件 【可以在程序中提前进行处理】
2.3 正确停止带来的好处
3.错误的停止方法
- 被弃用的
stop,suspend和resume方法 - 用
volatile设置boolean标记位
/**
* 错误的停止方法:用stop()来停止线程,会导致线程运行一半突然停止,
* 没办法完成一个基本单位的操作(一个连队),会造成脏数据(有的连队多领取少领取装备)。
*/
public class StopThread implements Runnable{
@Override
public void run() {
// 模拟指挥军队:一共有5个连队,每个连队10人,以连队为单位,发放武器弹药,叫到号的士兵前去领取
for (int i = 0; i < 5; i++) {
System.out.println("连队" + i + "开始领取武器");
for(int j = 0; j < 10; j++) {
System.out.println(j);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("连队" + i + "已经领取完毕");
}
}
public static void main(String[] args) {
Thread thread = new Thread(new StopThread());
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 立即停止领取的操作
thread.stop();
}
}
运行结果:
连队0开始领取武器
0
1
2
3
4
5
6
7
8
9
连队0已经领取完毕
连队1开始领取武器
0
1
2
3
4
5
6
会造成数据的错乱,比如连队 1 没有领取完
stop方法会释放掉所有的Monitor,被抛弃的原因是强制停止会造成数据的错乱。
suspend方法是带着锁休息的,这样就容易造成死锁。
3.2 用 volatile 设置 boolean 标记位
代码演示
/**
* 演示用volatile的局限:part2
* 陷入阻塞时, volatile是无法线程的
* 此例中,生产者的生产速度很快,消费者消费速度慢
* 所以阻塞队列满了以后,生产者会阻塞,等待消费者进一步消费
*/
public class WrongWayVolatileCantStop {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
// 生产者线程可以将阻塞队列塞满了(10个元素)
producerThread.start();
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while(consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了。");
// 一旦消费不需要更多数据了,我们应该让生产者也停下来
producer.canceled = true;
System.out.println(producer.canceled); // true
}
}
class Producer implements Runnable {
protected volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
// volatile 类型的变量具有可见性,修改后实时可见
// canceled = true 的时候退出 while 循环
while (num <= 100000 && !canceled) {
if (num % 100 == 0) {
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中了。");
}
num++;
}
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者停止运行");
}
}
}
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
if(Math.random() > 0.95) {
return false;
}
return true;
}
}
某一时刻跳出 while 循环,输出了 消费者不需要更多的数据了。 应该也执行了 producer.canceled = true 但是程序却并没有结束。并且注意生产者线程并没有执行到 finally 里面的语句。
即 volatile 这种方法没有办法把这个线程给停下来。
运行结果:
程序没有结束
0是100的倍数,被放到仓库中了。
100是100的倍数,被放到仓库中了。
200是100的倍数,被放到仓库中了。
300是100的倍数,被放到仓库中了。
400是100的倍数,被放到仓库中了。
500是100的倍数,被放到仓库中了。
600是100的倍数,被放到仓库中了。
700是100的倍数,被放到仓库中了。
800是100的倍数,被放到仓库中了。
900是100的倍数,被放到仓库中了。
0被消费了
1000是100的倍数,被放到仓库中了。
100被消费了
1100是100的倍数,被放到仓库中了。
1200是100的倍数,被放到仓库中了。
200被消费了
300被消费了
1300是100的倍数,被放到仓库中了。
1400是100的倍数,被放到仓库中了。
400被消费了
1500是100的倍数,被放到仓库中了。
500被消费了
600被消费了
1600是100的倍数,被放到仓库中了。
700被消费了
1700是100的倍数,被放到仓库中了。
1800是100的倍数,被放到仓库中了。
800被消费了
1900是100的倍数,被放到仓库中了。
900被消费了
2000是100的倍数,被放到仓库中了。
1000被消费了
消费者不需要更多数据了。
true
错误原因
这种方法一旦遇到了线程长时间阻塞的情况是无法唤醒的。因为每次检查 canceled 是在 while 判断条件的位置,但是实际阻塞的位置是在 storage.put(num) 这个位置,导致无法检查到 canceled 值的修改。
而使用 interrupt 来中断线程即便是遇到了 wait 和 sleep 等情况也可以及时地响应中断。即 interrupt 可以用来处理阻塞的情况。
修正方式
使用 interrupt
内部类要想被实例化必须要求它的内部类先被实例化。
代码:
/**
* 用中断来修复刚才的无尽等待问题
*/
public class WrongWayVolatileFixed {
public static void main(String[] args) throws InterruptedException {
WrongWayVolatileFixed body = new WrongWayVolatileFixed();
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
// 使用body来实例化内部类
Producer producer = body.new Producer(storage);
Thread producerThread = new Thread(producer);
// 生产者线程可以将阻塞队列塞满了(10个元素)
producerThread.start();
Thread.sleep(1000);
Consumer consumer = body.new Consumer(storage);
while(consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了。");
// 一旦消费不需要更多数据了,我们应该让生产者也停下来
producerThread.interrupt();
System.out.println(producer.canceled);
}
class Producer implements Runnable {
protected volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中了。");
}
num++;
}
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者停止运行");
}
}
}
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
if(Math.random() > 0.95) {
return false;
}
return true;
}
}
}
运行结果:
0是100的倍数,被放到仓库中了。
100是100的倍数,被放到仓库中了。
200是100的倍数,被放到仓库中了。
300是100的倍数,被放到仓库中了。
400是100的倍数,被放到仓库中了。
500是100的倍数,被放到仓库中了。
600是100的倍数,被放到仓库中了。
700是100的倍数,被放到仓库中了。
800是100的倍数,被放到仓库中了。
900是100的倍数,被放到仓库中了。
1000是100的倍数,被放到仓库中了。
0被消费了
100被消费了
1100是100的倍数,被放到仓库中了。
1200是100的倍数,被放到仓库中了。
200被消费了
300被消费了
1300是100的倍数,被放到仓库中了。
400被消费了
1400是100的倍数,被放到仓库中了。
500被消费了
1500是100的倍数,被放到仓库中了。
1600是100的倍数,被放到仓库中了。
600被消费了
1700是100的倍数,被放到仓库中了。
700被消费了
800被消费了
1800是100的倍数,被放到仓库中了。
1900是100的倍数,被放到仓库中了。
900被消费了
2000是100的倍数,被放到仓库中了。
1000被消费了
2100是100的倍数,被放到仓库中了。
1100被消费了
2200是100的倍数,被放到仓库中了。
1200被消费了
1300被消费了
2300是100的倍数,被放到仓库中了。
1400被消费了
2400是100的倍数,被放到仓库中了。
1500被消费了
2500是100的倍数,被放到仓库中了。
2600是100的倍数,被放到仓库中了。
1600被消费了
1700被消费了
2700是100的倍数,被放到仓库中了。
1800被消费了
2800是100的倍数,被放到仓库中了。
1900被消费了
2900是100的倍数,被放到仓库中了。
3000是100的倍数,被放到仓库中了。
2000被消费了
消费者不需要更多数据了。
生产者停止运行
false
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353)
at threadcoreknowledge.stopthread.volatiledemo.WrongWayVolatileFixed$Producer.run(WrongWayVolatileFixed.java:55)
at java.lang.Thread.run(Thread.java:748)
4.停止线程相关的重要函数解析
interrupt 方法
-
判断是否已被中断相关方法
-
static boolean interrupted()【返回线程是否被中断,并且直接将线程的中断状态设置为 false,也就是直接清清除了线程的中断状态,这也是唯一能清除线程状态的办法】public static boolean interrupted() { // true 表示清除中断状态 return currentThread().isInterrupted(true); } -
boolean isInterrupted()【返回线程是否被中断,不会做清除】public boolean isInterrupted() { // false 表示不清除中断状态 return isInterrupted(false); } -
Thread.interrupted()的目的对象 执行这个方法的线程
-
/**
* 注意Thread.interrupted()方法的目标对象是"当前线程",而不管方法来自于哪个对象
*/
public class RightWayInterrupted {
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
for(; ;) {
}
}
});
// 启动线程
threadOne.start();
// 设置中断表示
threadOne.interrupt();
// 获取中断标志【非静态】,不清除状态
System.out.println("isInterrupted:" + threadOne.isInterrupted()); // true
// 获取中断标志并重置【静态】,状态被清除了 [执行它的线程是main线程,main函数并没有被中断]
System.out.println("isInterrupted:" + threadOne.interrupted()); // false
// 获取中断标志并重置 [主线程的中断状态]
System.out.println("isInterrupted:" + Thread.interrupted()); // false
// 获取中断标志
System.out.println("isInterrupted:" + threadOne.isInterrupted()); // true
threadOne.join();
System.out.println("Main thread is over.");
}
}
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
// 使用同步获取到 blockerLock(与IO读写相关)
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
无论怎么样都需要调用 interrupt0 方法。
private native void interrupt0();
如果研究这些代码呢?如何分析 native 方法
- 进
github(也可以进openJDK网站)
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_Interrupt");
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
oop java_thread = JNIHandles::resolve_non_null(jthread);
MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
// We need to re-resolve the java_thread, since a GC might have happened during the
// acquire of the lock
JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
if (thr != NULL) {
Thread::interrupt(thr);
}
JVM_END
找到 thread.cpp 中的 interrupt
void Thread::interrupt(Thread* thread) {
trace("interrupt", thread);
debug_only(check_for_dangling_thread_pointer(thread);)
os::interrupt(thread);
}
5.彩蛋
JRE 和 JDK 是什么关系?Java 的 SDK 和 JDK 一样吗?Java7 是 Java SE7 吗,SE 是什么意思?Java8 和 JDK1.8 是什么关系,是同一个东西吗?
6.常见面试问题
如何停止一个线程
- 原理:用
interrupt来请求、好处 - 想停止线程,要请求方[发出一个请求信号]、被停止方[在循环中检查中断信号,并且在可能抛出InterruptedException的时候去处理这个信号]、子方法[子方法会被线程调用的,有两点最佳实践:①向上抛出这个Exception;②再次设置为中断状态] 被调用方相互配合
- 最后再说错误的方法:如果不用
interrupt的方法的时候,stop/suspend已废弃,volatile的boolean的无法处理长时间阻塞的情况
如何处理不可中断的阻塞 (例如抢锁时
ReentrantLock.lock()或者Socket I/O时无法响应中断,那应该怎么让该线程停止呢?)
不存在通用的解决方案,针对特定的问题使用特定的方法,尽量做到响应中断。