最近在复盘java并发的知识,借这篇文章总结下java的线程状态以及各状态下的中断影响。
中断简介
首先来看几个常用的中断Api,在此之前我们先来看一个本地方法
private native boolean isInterrupted(boolean ClearInterrupted);
这个本地方法是我们接下来要说的Api的底层关键实现,返回该线程中断标志位是否被标记。该方法中的参数ClearInterrupted,如果是true,表示会清除中断标志位,即调用后中断标志位由true变为false;如果是false表示不清除中断标志位
接着来看3个常用的中断Api
public void interrupt() 设置当前线程的中断标志位,仅此而已
public boolean isInterrupted() 判断当前线程中断标志位是否被标记为中断,不会清除标志位
public boolean isInterrupted() {
return isInterrupted(false);
}
可以看到底层就是调用的我们上边说过的本地方法,所以该方法不会清除标志位
public static boolean interrupted() 这是一个静态方法,返回当前线程是否标记中断,同时清除标志位
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
底层还是那个本地方法,不过这里标记了true,所以会清除标志位。同时需要特别主要注意的是,这里有一个currentThread(),这个方法返回的是调用这个方法时所在的线程体,而不是调用该方法的线程实例。
测试实例
thread.start();
thread.interrupt();
System.out.println(thread.isInterrupted());
System.out.println(thread.isInterrupted());
Thread.currentThread().interrupt();
System.out.println(Thread.interrupted());
System.out.println(Thread.interrupted());
true
true
true
false
可以看到结果验证了我们所说的,isInterrupted()不会清楚中断标志位,所以前两个返回true,随后将当前线程设置中断,第一次调用Thread.interrupted()返回true,第二次返回false,即中断标志位被清除。
接着来看下在各个线程状态,中断带来的影响
RUNNABLE
public static void main(String[] args) {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
while(true);
}
},"test");
thread.start();
System.out.println(thread.getName()+" "+thread.getState());
thread.interrupt();
System.out.println(thread.isInterrupted());
System.out.println(thread.getName()+" "+thread.getState());
}
test RUNNABLE
true
test RUNNABLE
可以看到,中断对RUNNABLE的test线程就只是将中断标志位置为true,仅此而已。不会抛出异常,线程状态也不变。
NEW | TERMINATEDE
public static void main(String[] args) {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
while(true);
}
},"test");
System.out.println(thread.getName()+" "+thread.getState());
thread.interrupt();
System.out.println(thread.isInterrupted());
System.out.println(thread.getName()+" "+thread.getState());
}
test NEW
false
test NEW
还是上一段代码,去掉start,此时test线程是NEW状态,同时中断标志位为false,可见中断对此状态的线程可以说完全不起作用。
WAITING
public class Twf implements Runnable {
@Override
public void run() {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("interrupted");
System.out.println(Thread.currentThread().isInterrupted());
}
}
}
public static void main(String[] args) {
Twf twf=new Twf();
Thread thread1=new Thread(twf,"test1");
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread1.getName()+" "+thread1.getState());
thread1.interrupt();
}
}
test1 WAITING
interrupted
false
可以看到,waiting状态的线程对中断很敏感,中断会抛出异常同时会清除中断标志位。同时别忘了,wait需要和synchronized配合使用!
至于为什么,看下一篇吧
Blocked
public class Twf implements Runnable {
static synchronized void test(){
while(true);
}
@Override
public void run() {
test();
}
public static void main(String[] args) {
Twf twf=new Twf();
Thread thread1=new Thread(twf,"test1");
Thread thread2=new Thread(twf,"test2");
thread1.start();
// 睡眠1s
thread2.start();
// 睡眠1s
System.out.println(thread2.getName()+" "+thread2.getState());
thread2.interrupt();
System.out.println(thread2.getName()+" "+thread2.getState());
System.out.println(thread2.isInterrupted());
}
}
test2 BLOCKED
test2 BLOCKED
true
可以看到中断对时间片Block也没有什么影响,只会把中断标志位置为true。
总结
以前学单片机总认为中断就是系统停下来去干别的事情,java中断仅仅是设置中断标记位。对于NEW|TERMINATED 线程,终端完全不起作用;对于RUNNABLE或者BLOCKED线程,只会将中断标志位设为true;WAITING线程对中断较为敏感,会抛出异常,同时会将中断标志位清除变为false。java中的中断设置标志位,接着的事情就留给程序员,根据中断这一条件去执行别的逻辑抑或是不管。
公众号:程序员二狗
每日原创文章,一起交流学习