浅析Java线程的正确停止

线程错误终止之destroy与stop方法

记得以前初学Java的时候,由于缺少对锁、同步、异步等这些线程的知识,想当然的以为destroy与stop方法都能正确的停止Java线程的执行。但是,后来随着工作的积累,以及对线程安全的一些理解,慢慢认识到这两个方法是有问题的,并且这两方法也早已在java doc上被指名是弃用的。

destroy()这个方法其实根本没干什么事情,只是抛出了一个NoSuchMethodError,所以说该方法无法终止线程,因此不能望文生意:

/**
     * Throws {@link NoSuchMethodError}.
     *
     * @deprecated This method was originally designed to destroy this
     *     thread without any cleanup. Any monitors it held would have
     *     remained locked. However, the method was never implemented.
     *     If if were to be implemented, it would be deadlock-prone in
     *     much the manner of {@link #suspend}. If the target thread held
     *     a lock protecting a critical system resource when it was
     *     destroyed, no thread could ever access this resource again.
     *     If another thread ever attempted to lock this resource, deadlock
     *     would result. 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>.
     * @throws NoSuchMethodError always
     */
    @Deprecated
    public void destroy() {
        throw new NoSuchMethodError();
    }
对于stop方法,其本质就是因为会 线程不安全 它会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。具体细节可以看官方的java doc;
Deprecated. 
This method was originally designed to destroy this thread without any cleanup. Any monitors it held would have remained locked. However, the method was never implemented. If if were to be implemented, it would be deadlock-prone in much the manner of suspend(). If the target thread held a lock protecting a critical system resource when it was destroyed, no thread could ever access this resource again. If another thread ever attempted to lock this resource, deadlock would result. Such deadlocks typically manifest themselves as "frozen" processes. For more information, see Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.

线程的正确终止

在上述的destroy和stop方法都一一被否定之后,那还有什么方式能够正确多终止线程呢?总的来说,在java中有两种解决方案:

  • 标志,在run方法中通过一个标记来进行结束,由于该方式很寻常就不做举例
  • interrupt,通过异常中断

下面就主要针对interrupt进行展开探讨。

/**
     * Interrupts this thread.
     *
     * <p> Unless the current thread is interrupting itself, which is
     * always permitted, the {@link #checkAccess() checkAccess} method
     * of this thread is invoked, which may cause a {@link
     * SecurityException} to be thrown.
     *
     * <p> If this thread is blocked in an invocation of the {@link
     * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
     * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
     * class, or of the {@link #join()}, {@link #join(long)}, {@link
     * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     *
     * <p> If this thread is blocked in an I/O operation upon an {@link
     * java.nio.channels.InterruptibleChannel </code>interruptible
     * channel<code>} then the channel will be closed, the thread's interrupt
     * status will be set, and the thread will receive a {@link
     * java.nio.channels.ClosedByInterruptException}.
     *
     * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
     * then the thread's interrupt status will be set and it will return
     * immediately from the selection operation, possibly with a non-zero
     * value, just as if the selector's {@link
     * java.nio.channels.Selector#wakeup wakeup} method were invoked.
     *
     * <p> If none of the previous conditions hold then this thread's interrupt
     * status will be set. </p>
     *
     * <p> Interrupting a thread that is not alive need not have any effect.
     *
     * @throws  SecurityException
     *          if the current thread cannot modify this thread
     *
     * @revised 6.0
     * @spec JSR-51
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
根据java源码摘出的interrupt方法注释和方法体的实现,可以归纳为:通过调用interrupt方法可以使得处于 阻塞状态的线程抛出一个异常,即interrupt方法可以用来中断一个正处于阻塞状态的线程;另外,改方法还会设置线程的中断状态(注:isInterrupted()可以用来查询中断状态)。

实践检验真理

interrupt能否中断处于非阻塞状态的线程

代码:

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		WorkThread wThread = new WorkThread();
		wThread.start();
		System.out.println("开始调用wThread.interrupt()");
		wThread.interrupt();
		System.out.println("结束调用wThread.interrupt()");
	}

}

class WorkThread extends Thread {
	public void run() {
		for (int i = 0; i < Byte.MAX_VALUE;) {
			System.out.println("工作线程运行" + (++i));
		}
	}
}
运行结果:


由于太长,只截取部分,真实情况是运行到127。从图中可以发现,当工作线程第一次打印“工作线程运行1”后,祝线程就执行完了interrupt方法,但是工作线程却没有停止依旧执行,直到for循环中变量i的值超出Byte.MAX_VALUE为止。

结论:interrupt无法中断非阻塞状态的线程。

但是,我门可以换个思路,既然之前提过interrupt方法会设置线程的中断状态,那么我门可以通过isInterrupt()来进行判断,从而中断线程。(本质上这种方案时标志中断)

上代码,只对WorkThread的条件循环增加一个标志判断——isInterrupt():

class WorkThread extends Thread {
	public void run() {
		for (int i = 0; i < Byte.MAX_VALUE && isInterrupted();) {
			System.out.println("工作线程运行" + (++i));
		}
	}
}
运行结果:


从结果上来看,工作线程一次打印都没有执行。说明interrupt配合isInterrupt()能够中断非阻塞状态的线程。注:本质还是标志中断

interrupt能否中断阻塞状态的线程

上代码:

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		WorkThread wThread = new WorkThread();
		wThread.start();
		System.out.println("开始调用wThread.interrupt()");
		wThread.interrupt();
		System.out.println("结束调用wThread.interrupt()");
	}

}

class WorkThread extends Thread {
	public void run() {
		System.out.println("工作线程sleep");
		for (int i = 0; i < Byte.MAX_VALUE;) {
			try {
				sleep(10 * 1000);
				System.out.println("工作线程运行"+(++i));
			} catch (InterruptedException e) {
				System.out.println("工作线程InterruptedException");
				break;
			}
		}
		System.out.println("工作线程运行完毕");
	}
}
运行结果:

从程序到运行结果来看,当工作线程进入sleep(即阻塞)的时候,调用interrupt方法,将会促使线程抛出异常。

结论:interrupt能够中断阻塞状态的线程。

总结

Java没有立即终止线程的机制,Java的Thread类提供的destroy和stop方法无法正确终止线程,只能通过标志或者interrup方法来进行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值