Java线程中断
在Java中,没有可以强制线程终止的方法。 然而, interrupt 方法可以用来请求终止线程。当对一个线程调用 interrupt 方法时, 线程的中断状态将被置位。
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();
}
每一个线程都具有 的 boolean 标志。 每个线程都应该不时地检査这个标志, 以判断线程是否被中断。
要想弄清中断状态是否被置位, 首先调用静态的 Thread.currentThread 方法获得当前线 程, 然后调用 islnterrupted 方法:
while (!Thread.currentThread().islnterrupted() && more work to do) {
do more work
}
如果线程被阻塞, islnterrupted方法就无法检测中断状态。这是产生 InterruptedExceptioii 异常的地 方。当在一个被阻塞的线程 (调用 sleep 或 wait ) 上调用 interrupt 方法时, 阻塞调用将会被 Interrupted Exception 异常中断。
在Thread类中,有两个非常类似的方法, interrupted 和 islnterrupted。Interrupted 方法是一个静态 方 法, 它检测当前的线程是否被中断。 而 且, 调 用 interrupted 方法会清除该线程的中断 状态。 另一方面, islnterrupted 方法是一个实例方法, 可用来检验是否有线程被中断。调 用这个方法不会改变中断状态。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
中断线程的两种方法
在 catch 子句中调用 Thread.currentThread().interrupt() 来设置中断状态。于是, 方便调用者 可以对其进行检测。
try {
sleep(delay);
}catch (InterruptedException e) {
Thread.currentThreadO-interruptO;
}
用 throws InterruptedException 标记你的方法, 不采用 try 语句 块捕获异常。于是, 调用者(或者, 最终的 run 方法)可以捕获这一异常。
void mySubTaskO throws InterruptedException {
sleep(3000);
}
Java线程状态
public enum State {
NEW,RUNNABLE, BLOCKED, WAITING,TIMED_WAITING, TERMINATED;
}
线程可以有如下 6 种状态:
New (新创建)
Runnable (可运行)
Blocked (被阻塞)
Waiting (等待)
Timed waiting (计时等待)
Terminated (被终止)
要确定一个线程的当前状态, 可调用 getState 方法。
新创建线程
当用 new 操作符创建一个新线程时, 如 newThread(runable), 该线程还没有开始运行。这意味 着它的状态是 new。 当一个线程处于新创建状态时, 程序还没有开始运行线程中的代码。 在 线程运行之前还有一些基础工作要做。
可运行状态
一旦调用 start 方法, 线程处于 runnable 状态。一个可运行的线桿可能正在运行也可能没 有运行, 这取决于操作系统给线程提供运行的时间。
一旦一个线程开始运行, 它不必始终保持运行。事实上, 运行中的线程被中断, 目的是 为了让其他线程获得运行机会。 线程调度的细节依赖于操作系统提供的服务。 抢占式调度系 统给每一个可运行线程一个时间片来执行任务。当时间片用完, 操作系统剥夺该线程的运行 权, 并给另一个线程运行机会。当选择下一个线程时, 操作系统考虑线程的优 先级。
现在所有的桌面以及服务器操作系统都使用抢占式调度。在具有多个处理器的机器上,每一个处理器运行一个线程, 可以有多个线程并行运行。 当然, 如果线程的数目多于处理器的数目, 调度器依然采用时间片机制。
被阻塞状态
当一个线程试图获取一个内部的对象锁(而不是 javiutiUoncurrent 库中的锁,) 而该 锁被其他线程持有, 则该线程进人阻塞状态。 当所有其他线程释放该锁, 并且线程调度器允许 本线程持有它的时候, 该线程将变成非阻塞状态。
等待状态
当线程等待另一个线程通知调度器一个条件时, 它自己进入等待状态。 在调用 Object.wait 方法或 Thread.join 方法, 或者是等待 java, util.concurrent 库中的 Lock 或 Condition 时, 就会出现这种情况。实际上, 被阻塞状态 与等待状态是有很大不同的。
public final void wait() throws InterruptedException {
wait(0);
}
public final void join() throws InterruptedException {
join(0);
}
join方法是等待另一个线程执行的结束,而wait方法则是需要别的线程调用notify或者notifyAll,然后根据线程的优先级选择。
public final native void notify();
public final native void notifyAll();
计时等待
有几个方法有一个超时参数。 调用它们导致线程进人计时等待(timed waiting) 状 态。 这一状态将一直保持到超时期满或者接收到适当的通知。 带有超时参数的方法有 Thread.sleep 和 Object.wait、Thread.join、Lock,tryLock 以及 Condition.await 的计时版。
public static native void sleep(long millis) throws InterruptedException;
public final native void wait(long timeout) throws InterruptedException;
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
被终止状态
线程因如下两个原因之一而被终止:
因为 run 方法正常退出而自然死亡。
因为一个没有捕获的异常终止了 run 方法而意外死亡。
线程属性
线程优先级
在 Java 程序设计语言中, 每一个线程有一个优先级。 默认情况下, 一+线程继承它的父 线程的优先级。 可以用 setPriority 方法提高或降低任何一个线程的优先级。 可以将优先级设 置为在 MIN_PRIORITY ( 在 Thread 类中定义为 1 ) 与 MAX_PRIORITY ( 定义为 10 ) 之间的 任何值。NORM_PRIORITY 被定义为 5。
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
每当线程调度器有机会选择新线程时, 它首先选择具有较高优先级的线程。 但是, 线程 优先级是高度依赖于系统的。当虚拟机依赖于宿主机平台的线程实现机制时, Java 线程的优 先级被映射到宿主机平台的优先级上, 优先级个数也许更多, 也许更少。
public static native void yield();
导致当前执行线程处于让步状态。 如果有其他的可运行线程具有至少与此线程同样高 的优先级, 那么这些线程接下来会被调度。 注意, 这是一个静态方法。
守护线程
可以调用
// 判断一个线程是否为守护线程
public final boolean isDaemon() {
return daemon;
}
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
将线程转换为守护线程(daemon thread。) 这样一个线程没有什么神奇。守护线程的唯一用途 是为其他线程提供服务。计时线程就是一个例子, 它定时地发送“ 计时器嘀嗒” 信号给其他 线程或清空过时的高速缓存项的线程。当只剩下守护线程时, 虚拟机就退出了,由于如果只 剩下守护线程, 就没必要继续运行程序了。
未捕获异常处理器
线程的 run 方法不能抛出任何受查异常, 但是, 非受査异常会导致线程终止。 在这种情 况下, 线程就死亡了。
public abstract void run(); // 没有抛出任何任何异常
但是, 不需要任何 catch 子句来处理可以被传播的异常。 相反, 就在线程死亡之前, 异 常被传递到一个用于未捕获异常的处理器。该处理器必须属于一个实现 Thread.UncaughtExceptionHandler 接口的类。 这个接口只有 —个方法。
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
可以用 setUncaughtExceptionHandler 方法为任何线程安装一个处理器。 也可以用 Thread 类的静态方法 setDefaultUncaughtExceptionHandler 为所有线程安装一个默认的处理器。 替换 处理器可以使用日志 API 发送未捕获异常的报告到日志文件。
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(
new RuntimePermission("setDefaultUncaughtExceptionHandler")
);
}
defaultUncaughtExceptionHandler = eh;
}
如果不安装默认的处理器, 默认的处理器为空。 但是, 如果不为独立的线程安装处理 器, 此时的处理器就是该线程的 ThreadGroup 对象。
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
// ThreadGroup实现的未捕获异常的处理器
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread ""
+ t.getName() + "" ");
e.printStackTrace(System.err);
}
}
}
}
ThreadGroup 类实现 Thread.UncaughtExceptionHandler 接口。 它的 uncaughtException 方 法做如下操作:
如果该线程组有父线程组, 那么父线程组的 uncaughtException 方法被调用。
否则, 如果 Thread.getDefaultExceptionHandler 方法返回一个非空的处理器, 则调用 该处理器。
否则, 如果 Throwable 是 ThreadDeath 的一个实例, 输出异常出现的线程名字。
否则, 线程的名字以及 Throwable 的栈轨迹被输出到 System.err 上。
这是你在程序中肯定看到过许多次的栈轨迹.