【java】如何优雅的停止线程?

【java】如何优雅的停止线程?

1、为什么要优雅的退出线程?

如果直接强制退出线程,可能会造成以下问题:

程序未按指定的逻辑执行(比如,我想累加到 10,结果到 6 的时候你就给我关闭了)可能导致死锁(程序在未释放锁的情况下被强制停止)可能导致资源未及时释放(程序还未释放资源,就被强制停止了)不会抛出异常,导致无法正确处理异常信息

2、如何停止线程?

大致有以下几种方法:

stop():强制退出,不推荐。interrupt():设置中断标记,尝试停止线程。volatile 关键字:类似于设置中断标记。线程池的 shutdownNow():尝试停止执行中的线程,并返回未开始执行的任务列表。

2.1、stop()(不推荐)

示例代码

private static void testStop() throws Throwable {

Thread t = new Thread(() -> {

while(true) {

System.out.println("执行1");

int count = 0;

String a = "";

try {

for (int i = 0; i < 2000000000; i++) {

count += 1;

a = "" + i; // 减慢执行时间

}

System.out.println("执行2");

if(Thread.currentThread().isInterrupted()) {

System.out.println("线程设置了中断标记,停止, count = " + count);

break;

}

} catch (Exception e) {

throw new RuntimeException(); // 这里也可以选择抛出异常

} finally {

System.out.println("finally 执行3, count = " + count + "\n");

}

}

});

t.start();

Thread.sleep(10);

t.stop(); // 强制关闭线程

t.join(); // 等待线程执行完毕

}

输出:

我们的预期是,任务执行完循环之后再退出线程,但是这里还没有走完循环就直接强制退出了。

2.2、interrupt()

给线程设置一个中断标记,线程执行的时候判断是否设置了中断标记,如果设置了那么就尝试退出线程。

示例代码

private static void testInterrupt() throws Throwable{

Thread t = new Thread(() -> {

while(true) {

long count = 0;

try {

long pre = System.currentTimeMillis();

for (int i = 0; i < 2000000000; i++) {

count ++;

}

long after = System.currentTimeMillis();

System.out.println("任务所需时间: " + (after - pre)); // 计算上述循环所花时间,大致为 80ms

if(Thread.currentThread().isInterrupted()) { // 判断是否设置了中断标记,如果设置了就退出循环

System.out.println("需要中断, count = " + count);

break;

}

} catch (Exception e) {

break; // 抛出异常

} finally {

System.out.println("finally 执行3, count = " + count + "\n");

}

}

});

t.start();

long pre = System.currentTimeMillis();

Thread.sleep(10); // 等待 10 毫秒就停止线程,看看需要更多时间的任务是否需要停止

long after = System.currentTimeMillis();

System.out.println("线程停止前后时间: " + (after - pre)); // 大致为 23ms

t.interrupt(); // 设置中断标记

t.join(); // 等待线程执行完毕

}

输出:

可以使用 isInterrupted() 判断当前线程是否设置了中断标记。

isInterrupted() 的返回值:

true:设置了中断标记,并且清除中断标记。false:没有设置中断标记,或者是中断标记在之前就被清除了。

2.3、volatile 关键字

volatile 关键字简述:

可见性:保证线程对一个变量的更改时,其他线程能够立即看到该变量的最新值。(变量不走线程的缓存,而是直接从内存中读写)禁止指令重排序(本文不做详述)

如:

private static boolean shouldStop = false;

示例代码

private static void testVolatile() throws Throwable{

Thread t = new Thread(() -> {

while(!shouldStop) {

System.out.println("执行1");

try {

Thread.sleep(1000);

System.out.println("执行2");

} catch (InterruptedException e) {

throw new RuntimeException(); // 这里也可以选择抛出异常

} finally {

System.out.println("finally 执行3\n");

}

}

});

t.start();

Thread.sleep(1500); // 等待 1.5 秒

shouldStop = true;

t.join(); // 等待线程执行完毕

}

输出:

如果还不了解线程状态的读者,可以阅读这篇文章:java线程的生命周期

2.4、线程池的 shutdownNow()

shutdownNow():线程池状态变为 STOP,不仅拒绝新的任务,还会去尝试中断正在运行的线程(类似于 interrupt()),并且返回未开始执行的任务列表。

代码示例

private static void testExecutors() throws Throwable {

ExecutorService executor = Executors.newSingleThreadExecutor();

Future future = executor.submit(() -> {

while (true) {

System.out.println("执行1");

try {

Thread.sleep(1000); // 线程进入 TIMED_WAITING 状态,该状态遇到了 interrupt 线程中断的时候,会抛出 InterruptedException

System.out.println("执行2");

} catch (InterruptedException e) {

throw new InterruptedException();

} finally {

System.out.println("finally 执行3\n");

}

}

});

Thread.sleep(1500); // 等待 1.5 秒

List tasks = executor.shutdownNow();// 尝试关闭线程池,并且尝试停止执行的任务,返回未开始执行的任务列表

future.get(); // 等待任务执行完毕

}

输出:

额外:区别于 shutdownNow

线程池还有一个 shutdown() 方法,该方法是将线程池状态改为 SHUTDOWN(拒绝新的任务,并且不会去停止在执行的任务执行,而是让他们正常执行关闭)

3、总结

优雅关闭线程的方法有以下几种:

interrupt():尝试去关闭线程volatile 关键字:中断标记shutdownNow():线程池相关方法,会去尝试关闭线程池中正在运行的线程。

上面这些方法都是优雅的关闭线程的方法,他们都是尝试去关闭线程而不是强制关闭线程。

相关推荐

Duke大学怎么样?就业资源丰富
365bet体育投注地

Duke大学怎么样?就业资源丰富

📅 07-23 👁️ 868
虾米音乐和网易云音乐哪个好(虾米音乐与网易云音乐,全面对比,揭秘哪个更胜一筹)
各种软件应该安放在哪个盘
beat365手机中文官方网站

各种软件应该安放在哪个盘

📅 08-17 👁️ 787
吉利帝豪具体如何?为什么销量一直领先?看完你就明白了
闯红灯后一般多久可以查到
365bet体育投注地

闯红灯后一般多久可以查到

📅 07-24 👁️ 4631
不结婚难收场!盘点11对韩剧「CP感超强」荧幕情侣,《Moving 异能》赵寅成、韩孝周超能力之吻心动爆表!
魔兽世界锻造指南1 - 600
365bet体育投注地

魔兽世界锻造指南1 - 600

📅 08-20 👁️ 2456
倩女幽魂家园如何送花?(倩女幽魂家园如何送花给好友)
beat365手机中文官方网站

倩女幽魂家园如何送花?(倩女幽魂家园如何送花给好友)

📅 06-30 👁️ 7091
成语词典
beat365手机中文官方网站

成语词典

📅 07-05 👁️ 8503