前言
- 2月份买的
airpods pro
右耳开始出现:稍微晃动就会有哒哒的声音,去售后检测,说是硬件问题,也有客户反馈,如果大家有这问题的,保内可以去免费换。。。
正题开始
在编程中,肯定对InterruptException
很熟悉,举个经常有的例子就是Thread.sleep(1000)
。那么,这个InterruptException
到底源于什么呢?什么时候会抛出呢?
中断与Thread.sleep(long)
等方法的关系
- 源自
Thread.interrupt()
方法注释(还有其他方法与中断有关系,详情看注释)。
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
- 重点在
interrupt status will be cleared and it will receive an InterruptedException
。
中断标识位将被清除,然后抛出
InterruptedException
。
- 关于中断标识位清除,后续代码说明。
Thread.interrupt()
- 可以看下源码怎么说,
Thread.interrupt()
,笔者比较懒,加上注释也很直白。
// Just to set the interrupt flag
- 可见,中断可以理解为线程的标识位属性,线程可以通过标识符判断是否被中断,根据判断结果进行响应。
Thread.interrupted()
- 判断当前线程是否被中断的方法有2种:
1、
Thread.interrupted()
2、Thread.currentThread().isInterrupted()
- 这2种方法,最后都会调用一个
native
方法,只是说ClearInterrupted
参数会不一样。
// Thread.interrupted() ClearInterrupted = true
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// Thread.currentThread().isInterrupted() ClearInterrupted = false
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* 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);
- 那么为什么要有
ClearInterrupted
的差异呢?可以看下代码。
结合代码说明ClearInterrupted的作用(实际不能这么写 只是为了说明)
Thread.interrupt()
重置中断标识位,为什么要重置标识位呢?例如,对一个线程一先一后调用了2次Thread.interrupt()时,可以知道第2次调用后的状态。- 测试代码:
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new InterruptRunnable());
thread.start();
// 第一次中断
thread.interrupt();
Thread.sleep(1000);
// 第二次中断
thread.interrupt();
}
private static class InterruptRunnable implements Runnable {
AtomicInteger interruptCount = new AtomicInteger(0);
@Override
public void run() {
while (true) {
if (Thread.interrupted()) {
System.out.println("被中断了" + interruptCount.incrementAndGet() + "次");
}
// 此处是无限循环 。。。
}
}
}
}
- 结果:
被中断了1次
被中断了2次
...无限循环 但是没继续输出
- 那么,不重置呢?很显然,第一次调用就把标识位置成
true
了,第2次调用形同虚设。。。显然就是无限循环。
private static class InterruptRunnable implements Runnable {
AtomicInteger interruptCount = new AtomicInteger(0);
@Override
public void run() {
while (true) {
if (Thread.interrupted()) {
System.out.println("被中断了" + interruptCount.incrementAndGet() + "次");
}
// 此处是无限循环 。。。
}
}
}
}
- 真·无限循环
被中断了880090次
被中断了880091次
被中断了880092次
被中断了880093次
被中断了880094次
被中断了880095次
... 手动暂停
与sleep结合下
那么,sleep
抛出InterruptionException
清除标识位又是什么含义呢?
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new SleepRunnable());
thread.start();
Thread.sleep(1000);
// 第一次中断
thread.interrupt();
}
private static class SleepRunnable implements Runnable {
@Override
public void run() {
int i = 0;
// 如果被中断跑出循环
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(500);
System.out.println(i++);
} catch (InterruptedException e) {
// sleep清除标识位
System.out.println("当前是否被中断?答案是:" + Thread.currentThread().isInterrupted());
}
}
System.out.println("跑出循环??");
}
}
- 没跑出循环
0
当前是否被中断?答案是:false
1
2
3
4
5
6
7
... 无限循环
结束线程的方式
- 经过上述分析,基本理解了中断的基本概念,以及容易忽略的知识点,那么中断的必要性在哪儿呢?关于结束线程
过期的stop
、suspend
、resume
这里,以stop
为例,我们使用代码看看为什么不能使用上述方法。
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopRunnable());
thread.start();
Thread.sleep(500);
thread.stop();
System.out.println(getInfo());
}
private static class StopRunnable implements Runnable {
@Override
public void run() {
getInfo();
}
}
private static Lock lock = new ReentrantLock();
private static int i = 0;
private static int getInfo() {
lock.lock();
try {
Thread.sleep(5000);
// 子线程没执行
i++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("释放锁");
}
return i;
}
- 结果分析:
stop
的调用,虽然释放了锁,但是方法内的关键操作没有执行,会导致随机结果。
释放锁
释放锁
1
volatile
+ interrupt
安全终止线程
public static void main(String[] args) throws Exception{
TestRunnable runnable = new TestRunnable();
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
runnable.cancel();
Thread.sleep(5);
thread.interrupt();
}
private static class TestRunnable implements Runnable {
private volatile boolean on = true;
@Override
public void run() {
int i = 0;
// 这里注意因为sleep清除标识位 不能使用Thread.interrupted()
while (on && !Thread.currentThread().isInterrupted()) {
System.out.println(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 中断异常处理
}
}
}
public void cancel() {
on = false;
}
}
小结
- 中断的基本概念和必要性
- 如何终止线程
参考
- Java并发编程的艺术
- 兄弟的博客 – 大CAT