线程的生命周期
通用线程状态
-
初始状态
线程已被创建,但是还不被允许分配CPU执行。
注意,这个被创建其实是属于
编程语言层面
的(比如Java语言中的new Thread()
),实际在操作系统里,真正的线程还没被创建。 -
可运行状态
线程可以分配CPU执行,这时,操作系统中线程已经被创建成功了。
-
运行状态
操作系统会为处在可运行状态的线程
分配CPU时间片
,处在可运行状态的线程,就会变为运行状态。 -
休眠状态
如果处在运行状态的线程调用
某个阻塞的API
或等待某个事件条件可用
,那么线程就会转换到休眠状态。注意:此时线程会释放CPU使用权,休眠的线程永远没有机会获得CPU使用权,只有当等待事件出现后,线程会从休眠状态转换到可运行状态。
-
终止状态
线程执行完
或者出现异常
(被interrupt不算)就会进入终止状态,正式走到生命的尽头,没有起死回生的机会。
Java线程状态
Java 语言中
-
将通用线程状态的
可运行状态
和运行状态
合并为Runnable
; -
将
休眠状态
细分为三种 (BLOCKED
、WAITING
、TIMED_WAITING
),这三种状态在操作系统的眼中都是休眠状态,同样不会获得CPU使用权
在Thread
的源码中,定义了一个枚举类State
:
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread.
* A thread in the runnable state is executing in the Java virtual machine
* but it may be waiting for other resources from the operating system such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* A thread in the waiting state is waiting for another thread to perform a particular action.
* For example, 1) a thread that has called <tt>Object.wait()</tt> on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on that object.
* 2) A thread that has called <tt>Thread.join()</tt> is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread. The thread has completed execution.
*/
TERMINATED;
}
NEW状态
当用关键字new
创建一个Thread对象(在没有start
之前,该线程根本不存在,这只是一个Java对象)时,此时它并不处于执行状态,因为没有调用start
方法启动该线程,那么线程的状态为NEW状态:
Thread thread = new Thread(() -> {});
System.out.println(thread.getState());
Runnable可运行、运行状态
Java将通用线程状态的可运行状态
和运行状态
合并为Runnable
!
线程对象进入RUNNABLE
状态必须调用start
方法,此时才是真正地在JVM进程中创建了一个线程
,线程一经启动并不是就立即开始执行,其运行与否需要听令于CPU的调度
。处于ready to run
状态,只是具备了执行的资格。
一旦CPU通过轮询或者其他方式,从任务可执行队列中选中了线程,那么此时它才能真正地执行自己的逻辑代码。如果由于CPU的调度器轮询,使该线程放弃执行,或者线程主动调用yield方法,会放弃CPU执行权,由running
进入ready to run
状态(此时线程状态显示仍为Runnable
)!
调用了start()
方法之后,线程就处在RUNNABLE
状态了:
Thread thread = new Thread(() -> {});
thread.start();
//Thread.sleep(1000);
System.out.println(thread.getState());
在该状态进入休眠状态的情况:
-
调用了
sleep
或者wait
等方法而加入了waitSet
中:- 调用不带时间参数的等待API或者
join()
,就会从RUNNABLE
状态进入到WAITING状态
。当被唤醒就会从WAITING
进入RUNNABLE
状态; - 调用带时间参数的等待API,自然就从
RUNNABLE
状态进入TIMED-WAITING状态
。当被唤醒或超时时间到就会从TIMED_WAITING
进入RUNNABLE
状态。
- 调用不带时间参数的等待API或者
-
进行某个阻塞的IO操作;
-
获取某个锁资源,从而加入到该锁的阻塞队列:
- 当且仅有(just only)一种情况会从
RUNNABLE
状态进入到BLOCKED状态
,就是线程在等待synchronized
内置隐式锁; - 如果等待的线程获取到了
synchronized
内置隐式锁,也就会从BLOCKED
状态变为RUNNABLE
状态了。
- 当且仅有(just only)一种情况会从
休眠状态
BLOCKED状态
等待synchronized
内置锁,就会处在BLOCKED
状态:
public class ThreadStateTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new DemoThread());
Thread thread2 = new Thread(new DemoThread());
thread1.start();
thread2.start();
// 防止两个线程都没有获得锁
Thread.sleep(100);
System.out.println("thread1.getState(): " + thread1.getState());
System.out.println("thread2.getState(): " + thread2.getState());
System.exit(0);
}
}
class DemoThread implements Runnable {
@Override
public void run() {
commonResource();
}
public static synchronized void commonResource() {
while (true) {}
}
}
输出结果:
thread1.getState(): RUNNABLE
thread2.getState(): BLOCKED
WAITING状态
调用线程的join()
等方法,从RUNNABLE
变为WAITING
状态:
public static void main(String[] args) throws InterruptedException {
Thread main = Thread.currentThread();
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
System.out.println(main.getState()); // 此时main线程是WAITING状态
});
thread2.start();
thread2.join();
}
TIMED-WAITING状态
调用了sleep(long)
等方法,线程从RUNNABLE
变为TIMED-WAITING
状态:
public static void main(String[] args) throws InterruptedException {
Thread thread3 = new Thread(() -> {
try {
Thread.sleep(3000); // 时间要比主线程睡眠时间长
} catch (InterruptedException e) {
// 为什么要调用interrupt方法?
Thread.currentThread().interrupt();
e.printStackTrace();
}
});
thread3.start();
Thread.sleep(1000);
System.out.println(thread3.getState());
}
在该状态中,线程的状态可以发生如下的状态转换:
- 直接进入
TERMINATED
状态 - 进入
RUNNABLE
状态:- 线程完成了指定时间的休眠;
- wait中的线程被其他线程notify/notifyAll唤醒;
- 线程获取到了某个锁资源;
- 线程阻塞的操作结束;
- 线程在阻塞过程中被打断,比如其他线程调用了interrupt方法。
TERMINATED状态
线程进入TERMINATED状态,意味着该线程的整个生命周期都结束了。
Thread thread = new Thread(() -> {});
thread.start();
Thread.sleep(1000); // 让线程thread先结束
System.out.println(thread.getState());
轻量级阻塞与重量级阻塞
- 能够被中断的阻塞称为轻量级阻塞,对应的线程状态是
WAITING
或者TIMED_WAITING
; - 而像
synchronized
这种不能被中断的阻塞称为重量级阻塞,对应的状态是BLOCKED
。
初始线程处于NEW
状态,调用start()
之后开始执行,进入RUNNING
或者RUNNABLE
状态。如果没有调用任何的阻塞函数,线程只会在RUNNING
和RUNNABLE
之间切换,也就是系统的时间片调度。这两种状态的切换是操作系统完成的,开发者基本没有机会介入,除了可以调用yield()
函数,放弃对CPU的占用。
一旦调用了图中的任何阻塞函数,线程就会进入WAITING
(无限期阻塞)或者TIMED_WAITING
状态(传入了一个时间参数,阻塞一个有限的时间)。如果使用了synchronized关键字
或者synchronized块
,则会进入BLOCKED状态。