浅析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 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 ThreadDeath 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 stop 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 interrupt method should be used to interrupt the wait. For more information, see <a target=_blank href="http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.

线程的正确终止

在上述的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结束,由于太长,只截取部分。从中可以发现,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方法来进行。

时间: 2024-12-28 10:55:08

浅析Java线程的正确停止的相关文章

浅析java线程和OS线程的关系

探究java线程和OS线程之间的联系 一.准备工作 1.查看linux创建线程的方法    man pthread_create 根据man的配置可知,pthread_create会创建一个线程,这个函数是Linux的函数,可以通过C或者C++调用,该函数在pthread.h中 2.查看openjdk版本, rpm -qa | grep jdk 3.卸载原始openJDK版本 rpm -e --nodeps  xxxxx 4.准备oracle jdk7/jdk8,官网可自行下载对应的linux安

浅析Java线程的三种实现

一.前言 java.lang.Thread类是java的线程类.当生成一个Thread类的对象后,一个新的线程就产生了.Java中每一个线程都是通过某个特定的Thread对象的方法run()来完成其操作的,方法run()称为线程体. 下面简单介绍Thread类的几种构造方法: public Thread() public Thread(Runnable target) public Thread(Runnable target,String name) public Thread(String

停止Java线程,小心interrupt()方法

程序是很简易的.然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的.难以发现的错误. 在本篇文章中,我们针对这些难题之一:如何中断一个正在运行的线程. 背景     中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作.线程是死亡.还是等待新的任务或是继续运行至下一步,就取决于这个程序.虽然初次看来它可能显得简单,但是,你必须进行一些预警以实现期望的结果.你最好还是牢记以下的几点告诫. 首先,忘掉

如何停止一个正在运行的java线程

与此问题相关的内容主要涉及三部分:已废弃的Thread.stop().迷惑的thread.interrupt系列.最佳实践Shared Variable. 已废弃的Thread.stop() @Deprecated public final void stop() { stop(new ThreadDeath()); } 如上是Hotspot JDK 7中的java.lang.Thread.stop()的代码,学习一下它的doc: 该方法天生是不安全的.使用thread.stop()停止一个线程

Java 线程浅析

一.什么是线程要理解什么线程,我么得先知道什么是进程.现代操作系统在运行一个程序时,会为其创建一个进程.例如启动eclipse.exe其实就是启动了win系统的一个进程.现代操作系统调度的最小单元就是线程,也叫轻量级进程,在一个进程里面包含多个线程,这些线程都有各自的计数器.堆栈等,并且能够共享内存变量.例如我们启动了一个eclipse进程,我们运行在其中的程序就可以理解为线程.二.为什么要使用线程(1)更多的处理器核心(可以运行更多的线程).(2)更快的响应时间(线程可以并行执行).(3)更好

如何正确停止线程

关于如何正确停止线程,这篇文章(how to stop thread)给出了一个很好的答案, 总结起来就下面3点(在停止线程时): 1. 使用violate boolean变量来标识线程是否停止 2. 停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性 3. 对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO 核心如下: If you are writing y

[转]JAVA线程停止的方法

有三种方法可以使终止线程. 1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止. 2.  使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend.resume一样,也可能发生不可预料的结果). 已废弃 3.  使用interrupt方法中断线程. 如何停止java的线程一直是一个困恼我们开发多线程程序的一个问题.这个问题最终在Java5的java.util.concurrent中得到了回答:使用interrupt(),让线程在run方法中停止. 简介

Java线程状态、线程停止、线程阻塞

线程状态(五种状态) Java 线程的生命周期包括创建,就绪,运行,阻塞,死亡5 个状态.一个 Java 线程总是处于这 5 个生命周期状态之一,并在一定条件下可以在不同状态之间进行转换 . 创建状态 (New Thread) 在 Java 语言中使用 new操作符创建一个线程后,该线程仅仅是一个空对象,它具备了线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态. 就绪状态 (Runnable) 使用 start()方法启动一个线程后,系统为该线程分配了除 CPU 外的所需资源,

JAVA线程安全之synchronized关键字的正确用法

JAVA线程安全关于synchronized关键字的用法,今天才知道原来我一直错了.以为用了synchronized关键字包住了代码就可以线程同步安全了. 测试了下.发现是完全的错了.synchronized必须正确的使用才是真正的线程安全...虽然知道这种写法,一直以为却由于懒而用了错误的方法. 看来基础还没有打好.仍需复习加强!工作中犯这种错误是不可原谅的,要知道使用synchronized关键字的地方都是数据敏感的!汗一把... 先贴代码: [java] view plaincopypri