1 简介
一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume方法 都已经被废弃了。
在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制——中断。
中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true;接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程要求这条线程中断,此时究竟该做什么需要你自己写代码实现。
每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断;
通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。
2 通过volatile变量实现
static volatile boolean flag = false; //中断标识
public static void main(String[] args) {
new Thread(()->{
while (true){
if(!flag){
System.out.println("线程正常执行");
}else{
System.out.println("线程执行结束");
break;
}
}
}).start();
try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
flag = true;
}).start();
}
执行结果
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程执行结束
3 通过原子类实现
static AtomicBoolean flag = new AtomicBoolean(false); //中断标识
public static void main(String[] args) {
new Thread(()->{
while (true){
if(!flag.get()){
System.out.println("线程正常执行");
}else{
System.out.println("线程执行结束");
break;
}
}
}).start();
try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
flag.set(true);
}).start();
}
4 Interrupted
4.1 api
4.2 简单示例
interrupt()和isInterrupted()interrupt()方法设置中断标志位为true ,isInterrupted()方法获取线程的中断标志位
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (!Thread.currentThread().isInterrupted()) {
System.out.println("线程正常执行");
} else {
System.out.println("线程执行结束,t1的中断标识" + Thread.currentThread().isInterrupted());
break;
}
}
});
Thread t2 = new Thread(() -> {
System.out.println("t1的中断标识------" + t1.isInterrupted());
t1.interrupt();
System.out.println("t1的中断标识------" + t1.isInterrupted());
});
t1.start();
t2.start();
}
执行结果,在t2中,调用t1.interrupt();只是设置t1的中断标志为true,不会停止t1线程,想要停止t1线程,只能在t1线程中自己编码去停止,这里我是不断的去获取这个标志位,当标志位为true时,停止了线程。
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
线程正常执行
t1的中断标识------false
线程正常执行
t1的中断标识------true
线程执行结束,t1的中断标识true
4.3 示例-证明interrupt()方法不会停止线程
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0;i < 200;i ++){
System.out.println("线程执行" + i + "| t1的中断标识为" + Thread.currentThread().isInterrupted());
}
});
Thread t2 = new Thread(() -> {
System.out.println("t1的中断标识------" + t1.isInterrupted());
try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
t1.interrupt();
System.out.println("t1的中断标识------" + t1.isInterrupted());
});
t1.start();
t2.start();
}
执行结果,发现,刚开始t1的中断标识是false,到69后面,t1的中断标识变为true了,但是t1线程还是在正常执行。
说明interrupt()方法只是设置了一个标志位,并不会停止该线程,想要停止线程,需要我们自己编码实现。
线程执行0| t1的中断标识为false 线程执行1| t1的中断标识为false 线程执行2| t1的中断标识为false 线程执行3| t1的中断标识为false 线程执行4| t1的中断标识为false t1的中断标识------false 线程执行5| t1的中断标识为false 线程执行6| t1的中断标识为false
。。。。。。。。。。。。。。。。
线程执行69| t1的中断标识为false
t1的中断标识------true
线程执行70| t1的中断标识为true
线程执行71| t1的中断标识为true
线程执行72| t1的中断标识为true
线程执行73| t1的中断标识为true
。。。。。。。。。。。。。。。。
线程执行195| t1的中断标识为true
线程执行196| t1的中断标识为true
线程执行197| t1的中断标识为true
线程执行198| t1的中断标识为true
线程执行199| t1的中断标识为true
4.4 调用某个线程的interrupt()方法,若该线程处于阻塞状态
如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),在别的线程中调用当前线程对象的interrupt方法,interrupt()设置的的true中断标识会被清除,变为false。线程也会退出被阻塞状态,并抛出一个InterruptedException异常,继续执行。
4.4.1 示例
在t1线程中调用了sleep方法
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0;i < 200;i ++){
if(!Thread.currentThread().isInterrupted()){
System.out.println("线程执行" + i + "| t1的中断标识为" + Thread.currentThread().isInterrupted());
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("线程结束" + "| t1的中断标识为" + Thread.currentThread().isInterrupted());
break;
}
}
});
Thread t2 = new Thread(() -> {
System.out.println("t1的中断标识------" + t1.isInterrupted());
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
t1.interrupt();
System.out.println("t1的中断标识------" + t1.isInterrupted());
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
});
t1.start();
t2.start();
}
执行结果
最开始,t1的中断标识为false,正常执行
执行到3时,调用了t1的interrupt()方法,t1的中断标识被设置为true
但是我们看4这里,t1的中断标识马上变为false
到了9这里,抛出了InterruptedException异常
继续正常执行
线程执行0| t1的中断标识为false
t1的中断标识------false
线程执行1| t1的中断标识为false
线程执行2| t1的中断标识为false
线程执行3| t1的中断标识为false
t1的中断标识------true
线程执行4| t1的中断标识为false
线程执行5| t1的中断标识为false
线程执行6| t1的中断标识为false
线程执行7| t1的中断标识为false
线程执行8| t1的中断标识为false
线程执行9| t1的中断标识为false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.ruoyi.weixin.user.MyTest.IntrrupterTest.IntrrupterTest04.lambda$main$0(IntrrupterTest04.java:16)
at java.lang.Thread.run(Thread.java:748)
线程执行10| t1的中断标识为false
线程执行11| t1的中断标识为false
线程执行12| t1的中断标识为false
线程执行13| t1的中断标识为false
线程执行14| t1的中断标识为false
。。。。。。。。。。。。。。。。。
线程执行195| t1的中断标识为false
线程执行196| t1的中断标识为false
线程执行197| t1的中断标识为false
线程执行198| t1的中断标识为false
线程执行199| t1的中断标识为false
4.4.2 处理上面的问题
只需要在异常处理中再调用下interrupt()方法即可
try {
Thread.sleep(2);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0;i < 200;i ++){
if(!Thread.currentThread().isInterrupted()){
System.out.println("线程执行" + i + "| t1的中断标识为" + Thread.currentThread().isInterrupted());
try {
Thread.sleep(2);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}else{
System.out.println("线程结束" + "| t1的中断标识为" + Thread.currentThread().isInterrupted());
break;
}
}
});
Thread t2 = new Thread(() -> {
System.out.println("t1的中断标识------" + t1.isInterrupted());
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
t1.interrupt();
System.out.println("t1的中断标识------" + t1.isInterrupted());
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
});
t1.start();
t2.start();
}
执行结果,发现线程结束了
线程执行0| t1的中断标识为false
t1的中断标识------false
线程执行1| t1的中断标识为false
线程执行2| t1的中断标识为false
线程执行3| t1的中断标识为false
t1的中断标识------true
线程结束| t1的中断标识为true
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.ruoyi.weixin.user.MyTest.IntrrupterTest.IntrrupterTest05.lambda$main$0(IntrrupterTest05.java:15)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
4.5 static interrupted()
public static void main(String[] args) throws InterruptedException
{
System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
System.out.println("111111");
Thread.currentThread().interrupt();
System.out.println("222222");
System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
}
执行结果
最开始两次调用Thread.interrupted(),发现都是默认false
再调用了interrupt()被设为true
第三次调用Thread.interrupted(),返回为true,且此时它把中断标识修改为false了
所以,第四次调用Thread.interrupted(),返回为false
main---false
main---false
111111
222222
main---true
main---false
5 小结
中断只是一种协同机制,修改中断标识位仅此而已,不是立刻stop打断
5.1 interrupt()方法
当对一个线程,调用 interrupt() 时:
① 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。
被设置中断标志的线程将继续正常运行,不受影响。所以, interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。
② 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),在别的线程中调用当前线程对象的interrupt方法,interrupt()设置的的true中断标识会被清除,变为false。线程也会退出被阻塞状态,并抛出一个InterruptedException异常,继续执行
5.2 isInterrupted
()获取线程的中断标识
5.3 static interrupted()
Thread.interrupted(); 判断线程是否被中断,并清除当前中断状态,这个方法做了两件事:
1 返回当前线程的中断状态
2 将当前线程的中断状态设为false