咱们都知道Java中实现线程的两种方式:继承Thread或者实现Runnable。不管是哪种方式最后都是操作的Thread类。本篇文章我们聊的就是正常活动中的Thread怎么停止,虽然Thread类提供了stop()方法,但是这种方法太暴力,不安全所以被弃用了。
为什么说stop()太暴力呢?我这里举个例子:一个餐厅是禁止抽烟的,但是一位男士进入餐厅掏出烟就抽,服务员发现后立马强制掐断男士手中的烟,大家可以想象后果是什么。这种方式就相当于将正在活动的Thread强制stop一样,太暴力。那服务员应该怎么做呢?
服务员是不是应该礼貌的告知抽香烟的男士:先生您好,我们餐厅是禁止抽烟的。如果男士明事理那就会掐断香烟,如果不明事理是不是就不会掐断香烟继续抽呀。这种协商机制导致的后果就是抽香烟的男士可能掐断香烟,也可能不会掐断香烟。Thread停止的方式也是类似,需要协商,假如t2线程想让t1线程停止,只能通知t1停止,但是t1是否停止只能看t1自己是否愿意停止了,咱们看看停止线程的三种方式。
1、通过volatile修饰的标识停止线程
volatile是Java中的关键字,用来修饰会被不同线程访问和修改的变量。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。
可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。volatile即可实现线程之间的可见性。
实例代码:
package com.lc.test01;
import java.util.concurrent.TimeUnit;
/**
* @author liuchao
* @date 2023/4/8
*/
public class StopThreadOne {
volatile static Boolean stopFlag = Boolean.FALSE;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (stopFlag) {
System.out.println(Thread.currentThread().getName() + ",线程被终止");
break;
}
System.out.println(Thread.currentThread().getName() + ",线程进行中");
}
}, "t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
stopFlag = Boolean.TRUE;
System.out.println("---已发出通知,告知t1线程停止");
}, "t2").start();
}
}
效果:
t1,线程进行中
t1,线程进行中
……
t1,线程进行中
—已发出通知,告知t1线程停止
t1,线程被终止
2、通过CAS中的AtomicBoolean 标识停止线程
CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制。CAS 操作包含三个操作数 — 内存位置、预期数值和新值。CAS 的实现逻辑是将内存位置处的数值与预期数值想比较,若相等,则将内存位置处的值替换为新值。若不相等,则不做任何操作。这种原子操作也可以保证写线程改变数据后,读线程立马能读取到改变后的数据。
实例:
package com.lc.test01;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author liuchao
* @date 2023/4/8
*/
public class StopThreadTwo {
static AtomicBoolean stopFlag = new AtomicBoolean(Boolean.FALSE);
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (stopFlag.get()) {
System.out.println(Thread.currentThread().getName() + ",线程被终止");
break;
}
System.out.println(Thread.currentThread().getName() + ",线程进行中");
}
}, "t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
stopFlag.set(Boolean.TRUE);
System.out.println("---已发出通知,告知t1线程停止");
}, "t2").start();
}
}
效果:
t1,线程进行中
t1,线程进行中
……
t1,线程进行中
—已发出通知,告知t1线程停止
t1,线程被终止
3、通过interrupt、isInterrupted方法配合停止线程
interrupt、isInterrupted两个方法都是Thread自带的api。
interrupt()是将一个线程的中断标识设置为true,通俗说就是告诉这个线程你需要中断。
isInterrupted()是判断线程的中断标识是否被设置为true
那使用的原理就是,t2线程将t1线程中断标识设置为true,t1线程判断中断标识是否为true,但是t1是否停止取决于t1自己是否想停止。
实例代码:
package com.lc.test01;
import java.util.concurrent.TimeUnit;
/**
* @author liuchao
* @date 2023/4/8
*/
public class StopThreadThree {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + ",线程被终止");
break;
}
System.out.println(Thread.currentThread().getName() + ",线程进行中");
}
}, "t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
t1.interrupt();
System.out.println("---已发出通知,告知t1线程停止");
}, "t2").start();
}
}
效果:
t1,线程进行中
t1,线程进行中
……
t1,线程进行中
—已发出通知,告知t1线程停止
t1,线程被终止
这种方式使用时,有个地方需要注意:
如果t1线程内部阻塞的调用wait() 、wait(long)或wait(long, int)方法,或者在join() , join(long) , join(long, int) , sleep(long) ,或sleep(long, int) ,那么它的中断状态将被清除,并且将收到一个InterruptedException 。需要在catch中重新将中断标识设置为true
package com.lc.test01;
import java.util.concurrent.TimeUnit;
/**
* @author liuchao
* @date 2023/4/8
*/
public class StopThreadThree {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + ",线程被终止");
break;
}
System.out.println(Thread.currentThread().getName() + ",线程进行中");
try {
TimeUnit.MILLISECONDS.sleep(5);
} catch (InterruptedException e) {
System.out.println("抛出异常" + e.getMessage());
//注意这里一定要重新将中断标识设置为true
Thread.currentThread().interrupt();
}
}
}, "t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
t1.interrupt();
System.out.println("---已发出通知,告知t1线程停止");
}, "t2").start();
}
}
4、总结
不管是那种方式停止线程,都是以协商而不是暴力,t2线程告知t1线程中断,t1是不会里面停止的,最终t1是否停止取决于t1是否做了中断判断。
暂无评论内容