震惊!这样终止线程,竟然会导致服务宕机?(下)

简介: 震惊!这样终止线程,竟然会导致服务宕机?(下)

这是因为 stop() 方法会释放此线程中的所有锁,导致程序执行紊乱,破坏了程序的原子操作逻辑。


以上的这些问题,导致了 JDK 废弃了 stop() 的方法,它的废弃源码如下:


/**
 * Forces the thread to stop executing.
 * <p>
 * If there is a security manager installed, its <code>checkAccess</code>
 * method is called with <code>this</code>
 * as its argument. This may result in a
 * <code>SecurityException</code> being raised (in the current thread).
 * <p>
 * If this thread is different from the current thread (that is, the current
 * thread is trying to stop a thread other than itself), the
 * security manager's <code>checkPermission</code> method (with a
 * <code>RuntimePermission("stopThread")</code> argument) is called in
 * addition.
 * Again, this may result in throwing a
 * <code>SecurityException</code> (in the current thread).
 * <p>
 * The thread represented by this thread is forced to stop whatever
 * it is doing abnormally and to throw a newly created
 * <code>ThreadDeath</code> object as an exception.
 * <p>
 * It is permitted to stop a thread that has not yet been started.
 * If the thread is eventually started, it immediately terminates.
 * <p>
 * An application should not normally try to catch
 * <code>ThreadDeath</code> unless it must do some extraordinary
 * cleanup operation (note that the throwing of
 * <code>ThreadDeath</code> causes <code>finally</code> clauses of
 * <code>try</code> statements to be executed before the thread
 * officially dies).  If a <code>catch</code> clause catches a
 * <code>ThreadDeath</code> object, it is important to rethrow the
 * object so that the thread actually dies.
 * <p>
 * The top-level error handler that reacts to otherwise uncaught
 * exceptions does not print out a message or otherwise notify the
 * application if the uncaught exception is an instance of
 * <code>ThreadDeath</code>.
 *
 * @exception  SecurityException  if the current thread cannot
 *               modify this thread.
 * @see        #interrupt()
 * @see        #checkAccess()
 * @see        #run()
 * @see        #start()
 * @see        ThreadDeath
 * @see        ThreadGroup#uncaughtException(Thread,Throwable)
 * @see        SecurityManager#checkAccess(Thread)
 * @see        SecurityManager#checkPermission
 * @deprecated This method is inherently unsafe.  Stopping a thread with
 *       Thread.stop causes it to unlock all of the monitors that it
 *       has locked (as a natural consequence of the unchecked
 *       <code>ThreadDeath</code> exception propagating up the stack).  If
 *       any of the objects previously protected by these monitors were in
 *       an inconsistent state, the damaged objects become visible to
 *       other threads, potentially resulting in arbitrary behavior.  Many
 *       uses of <code>stop</code> should be replaced by code that simply
 *       modifies some variable to indicate that the target thread should
 *       stop running.  The target thread should check this variable
 *       regularly, and return from its run method in an orderly fashion
 *       if the variable indicates that it is to stop running.  If the
 *       target thread waits for long periods (on a condition variable,
 *       for example), the <code>interrupt</code> method should be used to
 *       interrupt the wait.
 *       For more information, see
 *       <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
 *       are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
 */
@Deprecated
public final void stop() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        checkAccess();
        if (this != Thread.currentThread()) {
            security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
        }
    }
    // A zero status value corresponds to "NEW", it can't change to
    // not-NEW because we hold the lock.
    if (threadStatus != 0) {
        resume(); // Wake up thread if it was suspended; no-op otherwise
    }
    // The VM can handle all thread states
    stop0(new ThreadDeath());
}


可以看出 stop() 方法被 @Deprecated 注释修饰了,而被此注解修饰的代码表示为过时方法,不建议被使用。从 stop() 的备注信息可以看出,官方也不建议使用 stop() ,说它是一个非安全的方法。


正确终止线程


那如何终止线程呢?这里提供 2 个正确的方法:

  1. 设置退出标识退出线程;
  2. 使用 interrupt() 方法终止线程。


1.自定义退出标识


我们可以自定义一个布尔变量来标识是否需要退出线程,实现代码如下:


// 自定义退出标识退出线程
static class FlagThread extends Thread {
    public volatile boolean exit = false;
    public void run() {
        while (!exit) {
            // 执行正常的业务逻辑
        }
    }
}


可以看出我们使用了关键字 volatile 对线程进行了修饰,这样就可以保证多线程的执行安全了,在我们需要让线程退出时,只需要把变量 exit 赋值为 true 就可以了。


2.interrupt 终止线程


当我们使用 interrupt() 方法时,以上两个示例的执行结果就正常了,执行代码如下:


public class ThreadStopExample {
    public static void main(String[] args) throws InterruptedException {
        // 问题一:破坏了程序的完整性
        Thread t1 = new Thread(() -> {
            try {
                System.out.println("子线程开始执行");
                // 模拟业务处理
                Thread.sleep(1000);
            } catch (Exception e) { }
            // 伪代码:重要业务方法
            System.out.println("子线程的重要业务方法");
        });
        t1.start();
        // 让子线程先运行一点业务
        Thread.sleep(100);
        // 终止子线程
        t1.interrupt();
        // 等待一段时间,确保子线程“执行完”
        Thread.sleep(3000);
        System.out.println("主线程执行完成");
        // 问题二:破坏了原子逻辑
        MyThread myThread = new MyThread();
        Thread t2 = new Thread(myThread);
        // 开启线程
        t2.start();
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(myThread);
            t.start();
        }
        // 结束线程
        t2.interrupt();
    }
    /**
     * 自定义原子测试线程
     */
    static class MyThread implements Runnable {
        // 计数器
        int num = 0;
        @Override
        public void run() {
            // 同步代码块,保证原子操作
            synchronized (MyThread.class) {
                // 自增
                num++;
                try {
                    // 线程休眠 0.1 秒
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    System.out.println(e.getMessage());
                }
                // 自减
                num--;
                System.out.println(Thread.currentThread().getName() + " | num=" + num);
            }
        }
    }
}


以上程序的执行结果为:


子线程开始执行

子线程的重要业务方法

主线程执行完成

sleep interrupted

Thread-1 | num=0

Thread-9 | num=0

Thread-10 | num=0

Thread-7 | num=0

Thread-6 | num=0

Thread-5 | num=0

Thread-4 | num=0

Thread-2 | num=0

Thread-3 | num=0

Thread-11 | num=0

Thread-8 | num=0


可以看出以上的执行都符合我们的预期,这才是正确的终止线程的方式。


总结


本文我们讲了线程的三种终止方式,自定义退出标识的方式、使用 stop() 的方式或 interrupt() 的方式。其中 stop() 的方式会导致程序的完整性和原子性被破坏的问题,并且此方法被 JDK 标识为过期方法,不建议使用,而 interrupt() 方法无疑是最适合我们的终止线程的方式。

相关文章
|
3月前
|
消息中间件 监控 安全
服务Down机了,线程池中的数据如何保证不丢失?
在分布式系统与高并发应用开发中,服务的稳定性和数据的持久性是两个至关重要的考量点。当服务遭遇Down机时,如何确保线程池中处理的数据不丢失,是每一位开发者都需要深入思考的问题。以下,我将从几个关键方面分享如何在这种情况下保障数据的安全与完整性。
77 2
|
3月前
|
消息中间件 存储 Java
服务重启了,如何保证线程池中的数据不丢失?
【8月更文挑战第30天】为确保服务重启时线程池数据不丢失,可采用数据持久化(如数据库或文件存储)、使用可靠的任务队列(如消息队列或分布式任务队列系统)、状态监测与恢复机制,以及分布式锁等方式。这些方法能有效提高系统稳定性和可靠性,需根据具体需求选择合适方案并进行测试优化。
239 5
|
5月前
|
存储 安全 Java
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
72 0
|
6月前
用多线程的知识使电脑宕机
用多线程的知识使电脑宕机
30 0
|
Web App开发 消息中间件 Prometheus
Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控等!(一)
Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控等!
|
JSON 监控 安全
Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控等!(二)
Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控等!
|
Java 开发者 Spring
服务隔离机制信号量与线程池隔离|学习笔记
快速学习服务隔离机制信号量与线程池隔离
344 0
服务隔离机制信号量与线程池隔离|学习笔记
|
IDE Java 开发工具
震惊!这样终止线程,竟然会导致服务宕机?(上)
震惊!这样终止线程,竟然会导致服务宕机?(上)
143 0
震惊!这样终止线程,竟然会导致服务宕机?(上)
|
存储 安全
使用ExecutorService来停止线程服务
使用ExecutorService来停止线程服务
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
58 1
C++ 多线程之初识多线程