Java线程生命周期
Java线程的生命周期分为:新建(New)、就绪(Runnable)、阻塞(Blocked)、等待 (Waiting) 、计时等待(Timed_Waiting)、终止(Terminated) 六种状态,这些状态通过Thread.State
枚举类定义。
1. 新建状态(New)
-
当一个线程对象被创建但尚未调用
start()
方法时,线程处于新建状态。 -
在这个阶段,线程对象仅存在于内存中,但尚未与操作系统的底层线程关联。
-
可以为线程设置名称、优先级等属性,但线程未开始执行。
-
状态特点:
- 调用
start()
方法后会转移到就绪状态。 - 如果没有调用
start()
方法就试图调用join()
或interrupt()
,将抛出异常。
- 调用
-
示例:
public class ThreadLifecycleExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("Thread is running."));
// 线程处于新建状态
System.out.println("Thread State: " + thread.getState()); // 输出 NEW
}
}
2. 就绪状态(Runnable)
-
调用
start()
方法后,线程进入就绪状态。 -
此时线程已交给操作系统线程调度器管理,但未必立即运行。
-
线程调度器根据算法(如时间片轮转)决定何时将线程分配到CPU。
-
状态特点:
- 此状态不等同于“运行中”,线程可能随时等待被调度。
- 调用
start()
后,无法再次调用该方法,否则会抛出IllegalThreadStateException
。
-
示例:
public class ThreadLifecycleExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running.");
});
thread.start();
System.out.println("Thread State after start(): " + thread.getState()); // 输出 RUNNABLE
}
}
3. 运行状态(Running)
-
当线程获得CPU时间片后,线程进入运行状态。
-
此时线程执行其
run()
方法中的任务。 -
线程调度器可能在运行状态下暂停线程,并将其转移回就绪状态以让其他线程运行。
-
状态特点:
- 开发者无法显式设置线程为运行状态,状态完全由操作系统调度决定。
- 多线程环境下,运行状态的线程可能随时被挂起。
-
代码示例:
public class ThreadLifecycleExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread State: " + Thread.currentThread().getState()); // 输出 RUNNABLE 或运行中
});
thread.start();
}
}
4. 等待/阻塞/计时等待状态
线程可能因资源或时间限制无法继续运行时,会进入以下三种状态:
(1)等待状态(Waiting)
-
线程无限期地等待另一个线程唤醒它。
-
通常是通过
Object.wait()
或Thread.join()
进入。 -
唤醒方式包括调用
notify()
或notifyAll()
。 -
状态特点:
- 无法自行恢复,必须由另一个线程显式唤醒。
- 常用于线程间协作。
-
示例:
public class WaitingExample {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread thread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Thread is waiting.");
lock.wait(); // 线程进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread resumed.");
}
});
thread.start();
Thread.sleep(1000); // 主线程暂停1秒
synchronized (lock) {
lock.notify(); // 唤醒等待线程
}
}
}
(2)计时等待状态(Timed Waiting)
-
线程等待一段时间后会自动唤醒。
-
调用如
Thread.sleep()
、join(timeout)
或wait(timeout)
方法会进入该状态。 -
状态特点:
- 无需显式唤醒,时间结束后线程自动进入就绪状态。
- 常用于延时操作。
-
示例:
public class TimedWaitingExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
System.out.println("Thread is sleeping.");
Thread.sleep(2000); // 线程进入计时等待状态
System.out.println("Thread woke up.");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
}
(3)阻塞状态(Blocked)
-
当一个线程试图进入被其他线程持有的同步代码块时,会进入阻塞状态。
-
一旦锁被释放,线程将进入就绪状态等待调度。
-
状态特点:
- 阻塞是由于线程竞争共享资源而导致的。
- 常见于
synchronized
关键字或锁机制。
-
示例:
public class BlockedExample {
public static void main(String[] args) {
Object lock = new Object();
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(5000); // 保持锁5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 2 acquired the lock.");
}
});
thread1.start();
thread2.start();
}
}
5. 终止状态(Terminated)
-
描述:
- 当线程执行完成或因异常退出时,进入终止状态。
- 终止后的线程无法再次启动。
-
状态特点:
- 使用
isAlive()
方法可以检查线程是否仍存活。 - 线程终止后,它的资源会被JVM回收。
- 使用
-
代码示例:
public class TerminatedExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("Thread is running."));
thread.start();
try {
thread.join(); // 等待线程终止
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread State: " + thread.getState()); // 输出 TERMINATED
}
}
常见问题
1. 线程的生命周期包括哪几个阶段?**
New : 为线程分配内存并初始化其成员变量值
Runnable:JVM完成方法调用栈和程序计数器创建,等待线程调用和运行
Blocked:主动或被动放弃CPU使用权并暂停运行
Waiting:等待
Timed_Waiting:超时后会被自动唤醒
Terminated:正常或者异常结束或者手动结束
2. 在哪些情况下会出现线程阻塞?
线程阻塞(Blocked)通常发生在以下几种场景中:
- 等待锁:
- 一个线程试图进入由
synchronized
关键字保护的代码块或方法,但锁已被其他线程持有,当前线程将进入阻塞状态。 - 示例:
synchronized (lock) { // 当前线程执行时,其他线程试图进入会阻塞 }
- 一个线程试图进入由
- I/O操作:
- 线程等待输入/输出完成时,如从文件或网络读取数据。
- 线程阻塞工具类:
- 使用
Lock
或类似机制时,线程可能因为未能获取锁而被阻塞。 - 示例:
Lock lock = new ReentrantLock(); lock.lock(); // 其他线程会在这里阻塞
- 使用
- 其他同步机制:
- 使用
CountDownLatch
、Semaphore
等并发工具,线程可能会因未满足条件而阻塞。
- 使用
3. 线程在就绪状态下会立即执行吗?
不会 线程处于就绪状态时,已经准备好运行,但是否执行取决于线程调度器的安排。
线程调度器根据操作系统的调度策略(如时间片轮转或优先级)决定何时将线程分配到CPU。
如果有多个线程处于就绪状态,它们需要等待调度。
4. 什么是线程阻塞?
**线程阻塞(Blocked)**是指线程因为某种原因被挂起,无法继续执行,直到某些条件满足为止。
- 触发原因:
- 等待锁: 线程试图进入被其他线程持有的
synchronized
代码块或方法。 - 资源不可用: 线程等待特定资源(如文件、网络数据)。
- 线程竞争: 线程因并发控制工具(如锁、信号量)而受阻。
- 等待锁: 线程试图进入被其他线程持有的