为什么不能使用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
言之有物