安全地终止线程

2021-01-14 15:44:38 浏览数 (2)

安全地终止线程

概述


  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

废弃原因:不安全

0 人点赞