欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Thread.stop()为何废弃

程序员文章站 2022-04-01 15:50:34
...

官方给出的说明:

/** @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>.
  */

从注释可以看出使用Thread.stop()停掉一个线程将会导致所有已锁定的监听器被解锁(解锁的原因是当threaddeath异常在堆栈中传播时,监视器被解锁),这个之前被监听器锁定的对象被解锁,其他线程就能随意操作这个对象,将导致任何可能的结果。

官方给出的网页说明了不能捕获ThreadDeath异常并修复对象的原因:

  1. 一个线程几乎可以在任何地方抛出一个ThreadDeath异常。考虑到这一点,所有同步的方法和块都必须详细研究。
  2. 一个线程可以抛出第二个ThreadDeath异常,同时从第一个线程清除(在catch或finally子句中)。清理将不得不重复,直到它成功。确保这一点的代码将非常复杂。

所以捕获ThreadDeath异常是不可取的。

下面给出一个例子说明Thread.stop()将导致所有已锁定的监听器被解锁

public class StopTest {
    public static void main(String[] args) {
        final Object lock = new Object();

        try {
            Thread t0 = new Thread(() -> {
                try {
                    synchronized (lock) {
                        System.out.println("thread->" + Thread.currentThread().getName()
                                + " acquire lock.");
                        // sleep for 3s
                        Thread.sleep(3000);
                        System.out.println("thread->" + Thread.currentThread().getName()
                                + " release lock.");
                    }
                } catch (Throwable ex) {
                    System.out.println("Caught in run: " + ex);
                    ex.printStackTrace();
                }
            });

            Thread t1 = new Thread(() -> {
                synchronized (lock) {
                    System.out.println("thread->" + Thread.currentThread().getName()
                            + " acquire lock.");
                }
            });

            t0.start();
//            Thread.sleep(1000);
//            t0.stop();
            t1.start();
        } catch (Throwable e) {
            System.out.println("Caught in main: " + e);
            e.printStackTrace();
        }
    }
}

两个线程需要使用同一个对象,一开始lock被t0持有,经过三秒被t1持有,所以如果正常执行的话结果是这样的

thread->Thread-0 acquire lock.
thread->Thread-0 release lock.
thread->Thread-1 acquire lock.

但如果把两句注释的语句加上,一开始lock被t0持有,一秒后t0使用了stop结束线程,这个时候t0将会释放lock,所以t1持有了lock,此时,t0继续尝试操作,就会抛出ThreadDeath,结果如下

thread->Thread-0 acquire lock.
java.lang.ThreadDeath
Caught in run: java.lang.ThreadDeath
    at java.lang.Thread.stop(Thread.java:853)
thread->Thread-1 acquire lock.
    at com.minsming.www.StopTest.main(StopTest.java:40)

使用其他方法来替代Thread.stop()

  1. 使用interrupt
public class InterruptTest {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("thread->" + Thread.currentThread().getName()
                        + " run");
            }
            System.out.println("thread->" + Thread.currentThread().getName()
                    + " stop");
        });
        thread.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}
/* 
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 stop
*/
  1. 使用原子类或者volatile
public class StopTest2 {
    private static AtomicBoolean running = new AtomicBoolean(true);

    public static void main(String[] args) {
        try {
            Thread t0 = new Thread(() -> {
                while (StopTest2.running.get()) {
                    System.out.println("thread->" + Thread.currentThread().getName()
                            + " run");
                }
                System.out.println("thread->" + Thread.currentThread().getName()+  " stop");
            });

            t0.start();
            Thread.sleep(1000);
            StopTest2.running.set(false);
        } catch (Throwable t) {
            System.out.println("Caught in main: " + t);
            t.printStackTrace();
        }
    }
}
/*
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 run
thread->Thread-0 stop
*/

使用Thread.stop()会出现数据不同步,物理资源没及时关闭等危险,所以应该不用Thread.stop(),使用上述的方法替代它。

参考链接:

  1. http://www.baeldung.com/java-thread-stop
  2. https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
  3. http://roomfourteen224.iteye.com/blog/2191890
相关标签: 线程