线程正确停止方式-两阶段终止

92 阅读3分钟

错误的线程停止方式-stop

产生的问题

  • stop() 会真正杀死线程,如果这时候线程锁住了共享资源,线程被杀死后再也没有机会去释放锁资源,其它线程将永远不会获得锁
  • System.exit(int) 停止线程,会让整个程序停止。。

interrupt 两阶段终止模式优雅的停止线程

指的就是当希望结束一个线程的时候,送出一个终止请求,但是不会马上停止,做一些刷新工作。进入“终止处理中”,在该状态下,不会进行该线程日常工作任务的操作,而是进行一些终止操作。

public class RightStopThreadWithSleepEveryLoop {
    private Thread monitor;

    public static void main(String[] args) throws InterruptedException {
        RightStopThreadWithSleepEveryLoop rightStopThreadWithSleepEveryLoop = new RightStopThreadWithSleepEveryLoop();
        // 主线程运行
        rightStopThreadWithSleepEveryLoop.start();
        Thread.sleep(10);
        rightStopThreadWithSleepEveryLoop.stop();
    }


    private  void start() {
        monitor = new Thread(){
            @Override
            public void run() {
                while (true) {
                    Thread thread = Thread.currentThread();
                    if (thread.isInterrupted()) {
                        System.out.println("执行资源释放");
                        break;
                    }
                    try {
                        // 休息两秒在监控
                        Thread.sleep(3);
                        System.out.println("执行周期操作");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        // 在睡眠中收到终止请求
                        thread.interrupt();
                    }
                }}

        };
        monitor.start();
    }

    private void stop() {
        monitor.interrupt();
    }
}

细节:

  • isInterrupted() : 不会清除打断标记
  • Interrupted() : 会清除打断标记。

interrupt 影响 park 线程

LockSupport.park() 方法 阻塞执行线程

public class ParkThreadStopTest {

    private static void test() throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("park....");
            LockSupport.park();
            System.out.println("unpark.........");
            System.out.println("打断状态 : " + Thread.currentThread().isInterrupted());
        }, "t1");
        thread.start();
        // 让主线程 睡 1 秒
        Thread.sleep(1000);
        thread.interrupt();
    }

    public static void main(String[] args) throws InterruptedException {
        test();
    }
}

Thread.currentThread().isInterrupted() 之后,在 执行 park 不会产生之前的阻塞。中单标记位还在。
执行 Thread.interrupted 会产生与之前相同的阻塞。中断标记位消除,不中断,继续阻塞。

不推荐使用的方法

容易破坏同步代码块,不推荐使用。 都过时啦

  • stop() : 停止线程运行 interrupted 来停止
  • suspend() : 挂起线程 wait
  • resume() : 恢复线程运行 notify

守护线程

只要其他非守护线程执行结束,即使守护线程的代码没有执行玩,也会强制结束 在线程start前调用 setDaemon(true) 方法。 最常见的守护线程:gc线程

线程状态

image.png

  • 新建状态:该线程被创建,但没有和操作系统相关联(在Java中表现为未调用start() 方法)
  • 可运行状态; 该线程已经被创建(与操作系统线程相关联)、可以由CPU调度执行。
  • 运行状态:获取了CPU时间片运行的状态。
    • cpu 时间片用完,从运行状态转换到可运行状态,发生上下文切换
  • 阻塞状态:线程实际不会用到cpu、会导致线程上下文切换。IO阻塞操作
  • 等待状态
  • 计时等待状态
  • 终止状态:线程已经执行完毕,生命周期结束了,不会再转换为其他形态。

统筹规划 泡茶问题

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + "洗茶壶");
            TimeUnit.SECONDS.sleep(1);
            System.out.println(Thread.currentThread().getName() +  "烧开水");
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "lw");

    Thread t2 = new Thread(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + "洗茶杯");
            TimeUnit.SECONDS.sleep(1);
            System.out.println(Thread.currentThread().getName() + "拿茶叶");
            TimeUnit.SECONDS.sleep(1);
            System.out.println(Thread.currentThread().getName() + "洗茶杯");
            TimeUnit.SECONDS.sleep(2);
            t1.join();
            System.out.println(Thread.currentThread().getName() + "泡茶");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "xw");

    t1.start();
    t2.start();
}

上述代码 只能实现 xw 等待 lw 的执行结果,不能时 lw 等待 xw 的执行完毕,代码最好能适应两种情况。
模拟不了 lw 将 水壶 交给xw泡茶、或者说是 xw 把 茶叶 交给 lw 泡茶。