前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >安全地终止线程

安全地终止线程

作者头像
黑洞代码
发布2021-01-14 15:44:38
7340
发布2021-01-14 15:44:38
举报

安全地终止线程

概述


  1. 设置退出标识终止线程
  2. interrupt()方法终止线程
  3. 废弃的终止线程的方式
  4. 总结

第1节 设置退出标识终止线程

线程执行完后,将会终止。那么线程除了正常终止外,还有没有别的方式可以终止线程呢?

可以通过设置退出标识的方式使线程终止。

代码语言:javascript
复制
public class FlagThread extends Thread {
    /**
     * 退出标识
     */
    public volatile boolean exit = false;

    @Override
    public void run() {
        while (!exit);
        System.out.println("ThreadFlag线程退出");
    }

    public static void main(String[] args) throws Exception
{
        FlagThread threadFlag = new FlagThread();
        threadFlag.start();
        // 主线程延迟3秒
        sleep(3000);
        // todo 终止线程thread
        threadFlag.exit = true;
        // main线程放弃cpu使用权
        // 让threadFlag线程继续执行,直到threadFlag运行完
        threadFlag.join();
        System.out.println("线程退出!");
    }
}

在上面代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false。

在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是保证exit内存可见性,

也就是对exit的修改会立刻对其他线程可见。

第2节 interrupt()方法终止线程


1. 如果一个线程由于等待某些事件的发生而被阻塞,又该怎样停止该线程呢?

2. Thread.join()方法阻塞中的线程如何终止?

3. Thread.sleep()方法休眠中的线程如何终止?

4. ServerSocket.accept()方法阻塞中的线程如何终止?

5. 当线程阻塞时,使线程处于处于不可运行状态时,即使主程序中将该线程的退出标识设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即终止线程。

6. 这时可以使用Thread的interrupt()方法终止线程

代码语言:javascript
复制
public class InterruptThread extends Thread {
    /**
     * 退出标识
     */
    volatile boolean exit = false;

    @Override
    public void run() {

        while (!exit) {
            System.out.println(getName() + " is running");
            try {
                Thread.currentThread().join();
            } catch (InterruptedException e) {
                System.out.println("week up from block...");
                exit = true; // 在异常处理代码中修改共享变量的状态
            }
        }
        System.out.println(getName() + " is exiting...");
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptThread interruptThread = new InterruptThread();
        System.out.println("Starting thread...");
        interruptThread.start();
        Thread.sleep(3000);
        System.out.println("Interrupt thread...: " + interruptThread.getName());
        // 设置退出标识为true
        interruptThread.exit = true;
        // todo 阻塞时退出阻塞状态
        interruptThread.interrupt();
        // 主线程休眠3秒以便观察线程interruptThread的中断情况
        Thread.sleep(3000);
        System.out.println("Stopping application...");
    }
}

第3节 废弃的终止线程的方式


1. Thread.stop

2. Thread.suspend/Thread.resume

3. Runtime.runFinalizersOnExit

stop()方法的源码如下:

代码语言:javascript
复制
/**
     * 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()方法废弃了,Thread.stop()来强行终止线程,但是stop方法是很危险的,就像突然拔掉计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果。Thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeath这个Error,强行释放子线程持有的锁,导致被保护的资源出现线程安全问题。


suspend()方法的源码如下:

代码语言:javascript
复制
/**
     * Suspends this thread.
     * <p>
     * First, the <code>checkAccess</code> method of this thread is called
     * with no arguments. This may result in throwing a
     * <code>SecurityException </code>(in the current thread).
     * <p>
     * If the thread is alive, it is suspended and makes no further
     * progress unless and until it is resumed.
     *
     * @exception  SecurityException  if the current thread cannot modify
     *               this thread.
     * @see #checkAccess
     * @deprecated   This method has been deprecated, as it is
     *   inherently deadlock-prone.  If the target thread holds a lock on the
     *   monitor protecting a critical system resource when it is suspended, no
     *   thread can access this resource until the target thread is resumed. If
     *   the thread that would resume the target thread attempts to lock this
     *   monitor prior to calling <code>resume</code>, deadlock results.  Such
     *   deadlocks typically manifest themselves as "frozen" processes.
     *   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 suspend() {
        checkAccess();
        suspend0();
    }

suspend()方法使线程暂停,不会释放类似锁这样的资源。


resume()方法源码如下:

代码语言:javascript
复制
/**
     * Resumes a suspended thread.
     * <p>
     * First, the <code>checkAccess</code> method of this thread is called
     * with no arguments. This may result in throwing a
     * <code>SecurityException</code> (in the current thread).
     * <p>
     * If the thread is alive but suspended, it is resumed and is
     * permitted to make progress in its execution.
     *
     * @exception  SecurityException  if the current thread cannot modify this
     *               thread.
     * @see        #checkAccess
     * @see        #suspend()
     * @deprecated This method exists solely for use with {@link #suspend},
     *     which has been deprecated because it is deadlock-prone.
     *     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 resume() {
        checkAccess();
        resume0();
    }

resume()方法使线程恢复,如果之前没有使用suspend暂停线程,则不起作用。suspend()和resume()必须要成对出现,否则非常容易发生死锁。因为suspend方法并不会释放锁,如果使用suspend的目标线程对一个重要的系统资源持有锁,那么没任何线程可以使用这个资源直到要suspend的目标线程被resumed,如果一个线程在resume目标线程之前尝试持有这个重要的系统资源锁再去resume目标线程,这两条线程就相互死锁了。


runFinalizersOnExit()方法的源码如下:

代码语言:javascript
复制
/**
     * Enable or disable finalization on exit; doing so specifies that the
     * finalizers of all objects that have finalizers that have not yet been
     * automatically invoked are to be run before the Java runtime exits.
     * By default, finalization on exit is disabled.
     *
     * <p>If there is a security manager,
     * its <code>checkExit</code> method is first called
     * with 0 as its argument to ensure the exit is allowed.
     * This could result in a SecurityException.
     *
     * @param value true to enable finalization on exit, false to disable
     * @deprecated  This method is inherently unsafe.  It may result in
     *      finalizers being called on live objects while other threads are
     *      concurrently manipulating those objects, resulting in erratic
     *      behavior or deadlock.
     *
     * @throws  SecurityException
     *        if a security manager exists and its <code>checkExit</code>
     *        method doesn't allow the exit.
     *
     * @see     java.lang.Runtime#exit(int)
     * @see     java.lang.Runtime#gc()
     * @see     java.lang.SecurityManager#checkExit(int)
     * @since   JDK1.1
     */
    @Deprecated
    public static void runFinalizersOnExit(boolean value) {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            try {
                security.checkExit(0);
            } catch (SecurityException e) {
                throw new SecurityException("runFinalizersOnExit");
            }
        }
        Shutdown.setRunFinalizersOnExit(value);
    }

这个方法本身就是不安全的。它可能导致终结器(finalizers)被在活跃对象上被调用,而其他线程正在并发操作这些对象。而且,这个调用不是“线程安全”的,因为它设置了一个VM全局标志。

第4节


终止线程的有两种推荐的方式:

1. 使用退出标识

2. 设置中断

终止线程的废弃的方式有:

1. stop

2. suspend/resume

3. Runtime.runFinalizersOnExit

废弃原因:不安全

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 落叶飞翔的蜗牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档