优雅停止Thread(线程)的3种方式

咱们都知道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是否做了中断判断。

猜你喜欢

转载自blog.csdn.net/u011837804/article/details/130024259