安全地终止线程
概述
- 设置退出标识终止线程
- interrupt()方法终止线程
- 废弃的终止线程的方式
- 总结
第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
废弃原因:不安全