为什么不能使用Thread.stop()方法

不要直接使用Thread.stop()的原因有两点

  • stop() 方法的执行需要获取当前线程的锁。
  • stop() 方法一旦执行,当前线程上所有的锁会被立即释放,而且线程立刻中止,这可能导致数据安全问题。

stop() 方法的执行需要获取当前线程的锁

Thread.stop()的源码如下,可以看到最终会调用stop0()这个native方法。

@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());
}
   private native void stop0(Object o);

此时看不出什么,我们运行一个示例,这个例子中我们让这个数数并输出的线程运行了100ms便调用它的stop方法,
按照预期,数数的过程会立刻中止,然后打印出异常。然而结果却是每次数数的过程都会执行完成,然后才会打印出异常。

public class stopTest {
    public static void main(String[] args) {  
        try {  
            Thread t = new Thread() {  
                public synchronized void run() {  
                    try {  
                        long start=System.currentTimeMillis();  
                        for (int i = 0; i < 100000; i++)  
                            System.out.println("runing.." + i);  
                        System.out.println((System.currentTimeMillis()-start)/1000);  
                    } catch (Throwable ex) {  
                        System.out.println("Caught in run: " + ex);  
                        ex.printStackTrace();  
                    }  
                }  
            };  
            t.start();  
            // Give t time to get going...  
            Thread.sleep(100);  
            t.stop(); // EXPECT COMPILER WARNING  
        } catch (Throwable t) {  
            System.out.println("Caught in main: " + t);  
            t.printStackTrace();  
        }  
  
    }  
}
// 输出结果为
runing..99994
runing..99995
runing..99996
runing..99997
runing..99998
runing..99999
runing..100000

出现以上问题的缘由在于Thread的run方法使用了synchronized加锁了,而Thread.stop0()也同样需要获取锁才能执行。run方法在运行的时候便已经获取了锁,于是stop0()便一直被阻塞住,直到数数结束释放锁。
知道了问题所在,我们把锁修改一下,使用一个对象来作为线程的锁 final Object lock = new Object(); 使用如下代码来测试

public static void main(String[] args) {  
    final Object lock = new Object();  
    try {  
        Thread t0 = new Thread() {  
            public void run() {  
                try {  
                    synchronized (lock) {  
                        System.out.println("thread->" + getName()  
                                + " acquire lock.");  
                        sleep(3000);// sleep for 3s  
                        System.out.println("thread->" + getName()  
                                + " release lock.");  
                    }  
                } catch (Throwable ex) {  
                    System.out.println("Caught in run: " + ex);  
                    ex.printStackTrace();  
                }  
            }  
        };  

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

        t0.start();  
        // Give t time to get going...  
        Thread.sleep(100);  
        //t0.stop();  
        t1.start();  
    } catch (Throwable t) {  
        System.out.println("Caught in main: " + t);  
        t.printStackTrace();  
    }  
}

如果没有调用t0.stop()方法,t0,t1可以正常的运行,分别获取锁,输出为:

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

如果调用了t0.stop()方法,输出结果为

thread->Thread-0 acquire lock.
thread->Thread-1 acquire lock.
Caught in run: java.lang.ThreadDeath
java.lang.ThreadDeath
 at java.lang.Thread.stop(Thread.java:715)
 at com.pikzas.test.ThreadStopTest.main(ThreadStopTest.java:40)

可以看到t0直接中止了运行,抛出了异常,并且释放了锁。这种可以在任何地方中止运行的代码是不可接受的,因为它返回的对象状态是不可估计的。

举个例子:
我们希望对象保存一对数据i,j。这一对数据能同时候保持自增且两个变量保持相等。

public class DupValueTest extends Thread {
    public static void main(String[] args) throws InterruptedException {
        DupValueTest test = new DupValueTest();
        test.start();
        Thread.sleep(1000);
        test.stop();
        Thread.sleep(500);
        System.out.println("i: " + i + ", j: " + j);
    }

    private static int i, j = 0;

    @Override
    public synchronized void run() {
        while (true) {
            i++;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            j++;
        }
    }
}
//输出结果为
i: 10, j: 9

可以看到在调用stop之后,程序输出的结果是不符合预期的,i j的值变得不相等了,而我们的代码是有保证同步的。这样的运行结果是不能接受的。所以说stop方法不能被用来中止一个线程的运行。
那么该如何正确得中止一个线程呢?参考线程的中止。
原文地址:https://blog.csdn.net/kingzma/article/details/45739963

posted @ 2020-09-24 12:08  pikzas  阅读(842)  评论(0编辑  收藏  举报