参考:
学习 Java
线程的生命周期以及相关的函数
主要内容:
- 线程生命周期
getState
和isAlive
sleep
,yield
和join
interrupt
,isInterrupted
和interrupted
线程生命周期
参考:java.lang Enum Thread.State
A thread state. A thread can be in one of the following states:
* NEW - A thread that has not yet started is in this state.
* RUNNABLE - A thread executing in the Java virtual machine is in this state.
* BLOCKED - A thread that is blocked waiting for a monitor lock is in this state.
* WAITING - A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
* TIMED_WAITING - A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
* TERMINATED - A thread that has exited is in this state.
Java
定义线程生命周期共有 6
种状态:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITED
TERMINATED
各状态变化如下图所示:
NEW
使用 new
关键字创建新线程后,该线程就处于 新建 状态。此时,Java
虚拟机为其分配了内存,并初始化了成员变量
RUNNABLE
调用 start
方法后,线程就处于 可运行 状态。Java
虚拟机为其创建方法调用栈和程序计数器,但是线程是否已经运行取决于 Java
虚拟机线程调度器的调度,也就是说,当线程状态为 RUNNABLE
时,并不确定线程是否已经开始执行 run
方法
当没有足够的处理器资源时,线程调用 start
方法后并不会立即执行 run
方法。此时可以暂停当前线程 1
毫秒,即可让就绪线程立即开始运行
BLOCKED
,WAITING
和 TIMED_WAITING
内部对象锁
参考:《Java核心技术 卷I - 14.5.5 synchronized关键字》
从 Java 1.0
开始,每一个 Java
对象都有一个内部锁。如果方法使用 synchronized
关键字声明,即使用内部对象锁来保护方法。所以,必须先获取对象的内部锁,才能调用该方法
BLOCKED
当一个线程试图获取一个对象的内部锁,但该锁已被其它线程持有,那么,该线程就处于 阻塞 状态,直到其它线程释放该锁,并且线程调度器允许该线程持有,此时线程处于非阻塞状态
WAITING
和 TIMED_WAITING
当线程等待另一个线程通知调度器一个条件(condition
)时,该线程处于 等待 状态
即调用 Object.wait
或者 Thread.join
,或者使用 Lock
和 Condition
时,线程处于等待状态
如果线程除了等待另一个线程的通知外,还会计算等待时间,如果超过一定时间,自动回到 可运行 状态,那么此时线程进入的是 计时等待 状态
带有超时参数的方法:Thread.sleep
,Thread.wait
和 Thread.join
,以及 Lock.tryLock
和 Condition.await
TERMINATED
线程 终止 有两种情况:
- 正常运行
run
方法结束 - 在
run
方法中遇到一个未捕获异常而终止
《Java疯狂讲义 16.3 线程的生命周期》
在 《Java疯狂讲义》
中,将线程生命周期分为 5
个状态:
- 新建(
New
) - 就绪(
Runnable
) - 运行(
Running
) - 阻塞(
Blocked
) - 死亡(
Dead
)
其状态变化图如下图所示:
它将 Java
中的 可运行 状态分为了 就绪 和 运行 状态,等待处理器资源的属于 就绪 状态;已获得资源开始运行 run
方法的属于 运行 状态
将 阻塞/等待/计时等待 合并为 阻塞 状态
里面列出了多个处于阻塞状态的情况:
- 线程调用
sleep
方法主动放弃所占用的处理器资源 - 线程调用了一个阻塞式
IO
的方法,在该方法返回之前,该线程被阻塞 - 线程试图获取一个同步监视器,但该同步监视器被其它线程所占有
- 线程在等待某个通知(
notify
)
由上面可知,情况 1 属于 计时等待 状态,情况 2 属于 等待 状态,情况 3 属于 阻塞 状态,情况 4 属于 等待/计时等待 状态
解除上面处于 阻塞 状态的方法如下:
- 调用
sleep
方法的线程经过了指定时间 - 线程调用的阻塞式
IO
方法已经返回 - 线程成功地获得了试图获取的同步监视器
- 其它线程发出了通知
getState
和
isAlive
getState
和
isAlive
参考:
判断当前线程生命周期状态,可以使用方法 getState
:
/**
* Returns the state of this thread.
* This method is designed for use in monitoring of the system state,
* not for synchronization control.
*
* @return this thread's state.
* @since 1.5
*/
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
判断线程是否存活,使用方法 isAlive
:
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
*
* @return <code>true</code> if this thread is alive;
* <code>false</code> otherwise.
*/
public final native boolean isAlive();
sleep
,
yield
和
join
sleep
,
yield
和
join
参考:
sleep
使用方法 sleep
可以暂停当前执行线程指定时间,调用此方法后,线程从 可运行 状态转向 计时等待 状态,其有两个重载函数:
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException
通常仅指定暂停毫秒数(其精确性和准确性依赖于系统定时器和调度器)
/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
*
* @param millis
* the length of time to sleep in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
从重载方法实现中可看出,Java
系统仅将暂停时间精确到毫秒级
yield
方法 yield
用于提示系统当前线程愿意放弃处理器资源,回到就绪状态,此时如果没有优先级比当前线程高或者相等的就绪线程的话,系统会重新将处理器资源分配给当前线程
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield();
对于调用线程来说,yield
方法的使用并没有改变线程的状态(一直在 Runnable 状态)
join
join
方法有 3
种重载形式:
public final void join() throws InterruptedException
public final void join(long millis) throws InterruptedException
public final void join(long millis, int nanos) throws InterruptedException
如果线程 A
在线程 B
中调用了 join
方法,那么线程 B
需要等待线程 A
运行结束或者等待一段时间
public final void join() throws InterruptedException {
join(0);
}
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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;
}
}
}
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
从重载方法中可知,最终调用的是 join(long millis)
。如果没有任何参数,表示当前线程需要等待调用线程运行结束;如果有输入时间参数(毫秒单位),那么等待一段时间后即可运行
join
方法使当前线程从 运行时 状态进入 等待/计时等待 状态
interrupt
,
isInterrupted
和
interrupted
interrupt
,
isInterrupted
和
interrupted
参考:
线程中断相关的函数有如下 3
个:
public void interrupt()
public boolean isInterrupted()
public static boolean interrupted()
其使用场景并没有特别理解,线程对象调用 interrupt()
方法后,仅仅设置了中断位,并没有进一步的操作(进一步的操作需要自己去定义,可以忽略中断,也可以进行其它操作)
而且如果遇到线程处于 阻塞/等待/计时等待 的状态下,还会抛出 InterruptedExcpetion
在知乎上找了几个资料: