前言
Java并发知识体系持续更新:https://blog.csdn.net/m0_46144826/category_9881831.html
开始之前老规矩,把源码放上:
- Thread 源码解读注释:https://github.com/qianwei4712/JDK1.8.0.25-read/blob/master/src/main/java/java/lang/Thread.java
- Object 源码解读注释:https://github.com/qianwei4712/JDK1.8.0.25-read/blob/master/src/main/java/java/lang/Object.java
一个线程执行完毕之后会自动结束,如果在运行过程中发生异常也会提前结束。
线程中断是一个非常重要的概念,中断线程是取消任务最好的方法。
在 Java 的 Thread
类中,提供了以下三个关于线程中断的 public
方法:
void interrupt()
,中断线程。static boolean interrupted()
,测试当前线程是否已被中断。通过此方法可以清除线程的中断状态。boolean isInterrupted()
,测试线程是否已经中断,线程的中断状态不受该方法的影响。
这三个是线程的公共方法,但是它的最底层是两个 native 方法:
private native void interrupt0(); //中断线程
/**
* 测试某些线程是否已被中断。线程的中断状态不受此方法的影响。
* ClearInterrupted参数决定线程中断状态是否被重置,若为true则重置。
*/
private native boolean isInterrupted(boolean ClearInterrupted);
从这里就可以发现:
线程的中断状态,并不是由 Java 来决定。实际上,Thread 类中并没有维护线程的中断状态。
协作式和抢占式
然后,就有了下一个问题:
中断状态是个啥玩意儿???线程中断难道不是终止线程嘛?为什么还会有状态?Thread 类里面也没有这个状态变量啊,难道操作系统内给线程添加了这个状态?
所以,又又又又得百度去咯。。。根据各位大佬们的博客,个人感觉应该是这样的:
- 明确线程中断状态不是 Thread 类的标志位,而是操作系统中对线程的中断标志。
这一结论来自 native
方法:
/**
* 测试某些线程是否已被中断。线程的中断状态不受此方法的影响。
* ClearInterrupted参数决定线程中断状态是否被重置,若为true则重置。
*/
private native boolean isInterrupted(boolean ClearInterrupted);
很明显了,在操作系统对线程的管理中,确实存在一个中断状态的标志位。
- 操作系统调度线程的方式:协作式、抢占式
- 协作式:线程自己的工作执行完后,要主动通知调度切换到另一个线程上。 如果一个线程编写有问题,一直不告知系统进行线程切换,那么程序就会一直阻塞在那里。线程的执行时间由自身掌控。
- 抢占式:线程将由调度来分配执行时间,线程的切换不由线程自身决定。 不会出现一个线程导致整个进程阻塞的问题。
Java 的线程中断,也作为协作式中断,调用 interrupt()
方法最终也只是更改操作系统中的中断标志位,线程是否中断,由线程自身决定。
isInterrupted()
测试线程是否已经中断,线程的中断状态不受该方法的影响。
/**
* 测试此线程是否已被中断。线程的中断状态不受此方法的影响。
* <p>如果中断时,线程并没有存活,那么该方法返回 false。意思就是,如果线程还没有 start 启动,或者已经消亡,那么返回依然是 false.
* @return 如果该线程已被中断,返回true;否则返回 false
*/
public boolean isInterrupted() {
//不清除中断状态
return isInterrupted(false);
}
这是最基础的判断中断状态,且不更改 OS 线程中断标志位。可以对照上面的 native 方法阅读。特别的记一下这两句:
如果线程还没有 start 启动,或者已经消亡,那么返回依然是 false。中断状态只代表是否有线程调用中断方法。
举个栗子看下效果就行:
public static class Example extends Thread{
@Override
public void run() {
while (!isInterrupted()){
}
System.out.println("线程中断");
}
}
然后 main 启动:
public static void main(String[] args) throws Exception {
Example example = new Example();
System.out.println("当前线程 example 尚未启动,此时状态:" + example.isInterrupted());
example.start();
System.out.println("当前线程 example 启动且尚未调用 interrupt,此时状态:" + example.isInterrupted());
example.interrupt();
System.out.println("当前线程 example 启动且调用 interrupt,此时状态:" + example.isInterrupted());
Thread.sleep(5000);
System.out.println("等待 5 秒,example 应该已经消亡,此时状态:" + example.isInterrupted());
}
输出状态为:
当前线程 example 尚未启动,此时状态:false
当前线程 example 启动且尚未调用 interrupt,此时状态:false
当前线程 example 启动且调用 interrupt,此时状态:true
线程中断
等待 5 秒,example 应该已经消亡,此时状态:false
interrupt()
中断线程
先看下源码,以及官方(我自个的渣渣翻译)注释:
/**
* 中断此线程。
* <p>线程可以中断自身,这是允许的。在这种情况下,不用进行安全性验证({@link #checkAccess() checkAccess} 方法检测)
* <p>若当前线程由于 wait() 方法阻塞,或者由于join()、sleep()方法,然后线程的中断状态将被清除,并且将收到 {@link InterruptedException}。
* <p>如果线程由于 IO操作({@link java.nio.channels.InterruptibleChannel InterruptibleChannel})阻塞,那么通道 channel 将会关闭,
* 并且线程的中断状态将被设置,线程将收到一个 {@link java.nio.channels.ClosedByInterruptException} 异常。
* <p>如果线程由于在 {@link java.nio.channels.Selector} 中而阻塞,那么线程的中断状态将会被设置,它将立即从选择操作中返回。
*该值可能是一个非零值,就像调用选择器的{@link java.nio.channels.Selector#wakeupakeup}方法一样。
*
* <p>如果上述条件均不成立,则将设置该线程的中断状态。</p>
* <p>中断未运行的线程不必产生任何作用。
* @throws SecurityException 如果当前线程无法修改此线程
*/
public void interrupt() {
//如果调用中断的是线程自身,则不需要进行安全性判断
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 只是设置中断标志
b.interrupt(this);
return;
}
}
interrupt0();
}
这个方法的作用,在上面的栗子中已经有过体现了,接着来测试下源码中提到的异常:
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println("线程 sleep 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
thread.interrupt();
System.out.println("main 线程调用 interrupt 结束");
System.out.println("线程抛出了中断异常,此时状态:" + thread.isInterrupted());
Thread.sleep(5000);
System.out.println("等待 5 秒,线程应该已经消亡,此时状态:" + thread.isInterrupted());
}
运行结果:
main 线程调用 interrupt 结束
线程抛出了中断异常,此时状态:true
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at thread.InterruptTest.lambda$test2$0(InterruptTest.java:20)
at thread.InterruptTest$$Lambda$1/791452441.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
等待 5 秒,线程应该已经消亡,此时状态:false
在 sleep 过程中调用中断,抛出中断异常。。线程不会往下执行。
虽然抛出了异常,但是线程的中断状态确实设置成功了。。。只是线程被立刻从 sleep 中唤醒。
嗯,这两这个好理解。。。。。
interrupted()
测试当前线程是否已被中断。通过此方法可以清除线程的中断状态。
/**
* 测试当前线程是否已被中断。
* 通过此方法可以清除线程的中断状态.
* 换句话说,如果此方法要连续调用两次,则第二个调用将返回false(除非当前线程在第一个调用清除了它的中断状态之后,且在第二个调用对其进行检查之前再次中断)
* <p>如果中断时,线程并没有存活,那么该方法返回 false
* @return 如果该线程已被中断,返回true;否则返回 false
*/
public static boolean interrupted() {
//清除线程的中断状态
return currentThread().isInterrupted(true);
}
说实话,这个方法的作用,实在是有点迷。。。。
public static class Example2 extends Thread{
@Override
public void run() {
System.out.println("调用 interrupt 方法,线程中断");
interrupt();
System.out.println("尚未调用 interrupted 方法,此时线程中断状态:" + isInterrupted());
System.out.println("线程第 1 次调用 interrupted 方法,方法返回:" + Thread.interrupted());
System.out.println("线程第 1 次调用 interrupted 方法结束后,此时线程中断状态:" + isInterrupted());
System.out.println("线程第 2 次调用 interrupted 方法,方法返回:" + Thread.interrupted());
System.out.println("线程第 2 次调用 interrupted 方法结束后,此时线程中断状态:" + isInterrupted());
System.out.println("线程第 3 次调用 interrupted 方法,方法返回:" + Thread.interrupted());
System.out.println("线程第 3 次调用 interrupted 方法结束后,此时线程中断状态:" + isInterrupted());
}
}
mian 方法测试:
public static void main(String[] args) throws Exception {
Example2 example = new Example2();
example.start();
}
控制台输出结果为:
调用 interrupt 方法,线程中断
尚未调用 interrupted 方法,此时线程中断状态:true
线程第 1 次调用 interrupted 方法,方法返回:true
线程第 1 次调用 interrupted 方法结束后,此时线程中断状态:false
线程第 2 次调用 interrupted 方法,方法返回:false
线程第 2 次调用 interrupted 方法结束后,此时线程中断状态:false
线程第 3 次调用 interrupted 方法,方法返回:false
线程第 3 次调用 interrupted 方法结束后,此时线程中断状态:false
乍一看,感觉需要挠头。。。这个方法的作用也忒不明显了。。。 java doc
也不讲的不清楚。
仔细一想,是这样的:
- 作为一个
static
方法,作用于当前线程 - 调用
static boolean interrupted()
方法后,先将线程的中断状态设置为 false,再返回原先的中断状态
参考文章
https://blog.csdn.net/oldshaui/article/details/106952102
https://blog.csdn.net/qq_41901915/article/details/103654263
https://www.cnblogs.com/L-a-u-r-a/p/8575217.html
https://blog.csdn.net/tianjindong0804/article/details/105134182
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/10302.html