【Java第70集】java线程的生命周期详解

文章目录

  • 一、Java线程的生命周期
    • 1. NEW(新建)
    • 2. RUNNABLE(可运行)
    • 3. BLOCKED(阻塞)
    • 4. WAITING(无限等待)
    • 5. TIMED_WAITING(计时等待)
    • 6. TERMINATED(终止)
  • 二、Java线程状态的转换总览
  • 三、Java线程状态的转换详细介绍
    • 1. NEW → RUNNABLE
    • 2. RUNNABLE ↔ BLOCKED
    • 3. RUNNABLE → WAITING
    • 4. RUNNABLE → TIMED_WAITING
    • 5. RUNNABLE → TERMINATED
    • 6. WAITING → RUNNABLE
    • 7. TIMED_WAITING → RUNNABLE
  • 四、操作系统线程的生命周期
    • 1. 新建(New)
    • 2. 就绪(Ready/Runnable)
    • 3. 运行(Running)
    • 4. 阻塞(Blocked/Waiting)
    • 5. 终止(Terminated)
  • 五、Java线程与操作系统线程的关系
  • 六、实际应用中的注意事项
    • 1. 避免死锁
    • 2. 正确使用同步机制
    • 3. 线程中断
    • 4. 使用线程池管理线程
  • 七、总结

Java线程的生命周期是指线程从创建到终止的整个过程。Java中线程的状态由 java.lang.Thread.State 枚举类定义,包含新建(New)就绪(Runnable)阻塞(Blocked)等待 (Waiting) 计时等待(Timed_Waiting)终止(Terminated) 这6种核心状态。本文将深入解析Java线程的生命周期、六种核心状态及其转换规则,并结合代码示例帮助读者掌握实际应用技巧。

一、Java线程的生命周期

  • Java线程的生命周期图:
    【Java第70集】java线程的生命周期详解_第1张图片

1. NEW(新建)

  • 定义:线程对象被创建但尚未启动。
  • 触发条件
    • 通过 new Thread() 创建线程对象,但未调用 start() 方法。
  • 特点
    • 线程未与操作系统线程关联,仅是一个Java对象实例。
    • 无法执行任何代码,直到调用 start()
  • 示例
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(可运行)

  • 定义:线程已启动,等待CPU调度运行。
  • 触发条件
    • 调用 start() 方法后。
  • 子状态
    • 就绪(Ready):线程准备运行,等待CPU时间片分配。
    • 运行(Running):线程获得CPU时间片,正在执行代码。
  • 特点
    • 在JVM层面,RUNNABLE 包含操作系统中的 “就绪” 和 “运行” 状态。
    • 如果计算机有多个处理器,多个线程可以并行执行。
  • 示例
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. BLOCKED(阻塞)

  • 定义:线程因等待锁而无法继续执行。
  • 触发条件
    • 线程试图获取一个已被其他线程持有的对象锁(如 synchronized 同步块)。
  • 特点
    • 阻塞状态的线程不会占用CPU资源。
    • 当持有锁的线程释放锁后,阻塞的线程将转回 RUNNABLE 状态。
  • 示例
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();
    }
}


4. WAITING(无限等待)

  • 定义:线程因调用 wait()join()LockSupport.park() 进入无限等待状态。
  • 触发条件
    • 调用 Object.wait():等待其他线程调用 notify()/notifyAll()
    • 调用 Thread.join():等待某个线程终止。
    • 调用 LockSupport.park():线程暂停。
  • 特点
    • 线程必须被显式唤醒(如调用 notify())才能恢复执行。
  • 示例
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(); // 唤醒等待线程
        }
    }
}


5. TIMED_WAITING(计时等待)

  • 定义:线程因调用带有超时参数的方法进入限时等待状态。
  • 触发条件
    • 调用 Thread.sleep(millis):线程休眠指定时间。
    • 调用 Object.wait(millis):等待指定时间后自动唤醒。
    • 调用 Thread.join(millis):等待指定时间后放弃。
    • 调用 LockSupport.parkNanos(nanos):暂停指定时间。
  • 特点
    • 超时后线程会自动转回 RUNNABLE 状态。
  • 示例
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();
    }
}


6. TERMINATED(终止)

  • 定义:线程完成执行或因异常结束。
  • 触发条件
    • 正常执行完 run() 方法。
    • 抛出未捕获的异常。
    • 被其他线程中断。
  • 特点
    • 线程对象仍然存在,但无法重新启动。
  • 示例
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
    }
}


二、Java线程状态的转换总览

源状态 目标状态 触发条件 典型方法/场景
NEW RUNNABLE 调用 start() 方法 Thread t = new Thread(); t.start();
RUNNABLE BLOCKED 竞争 synchronized 锁失败 多线程访问同步代码块或方法
RUNNABLE WAITING 调用 wait()join()LockSupport.park() Object.wait()Thread.join()LockSupport.park()
RUNNABLE TIMED_WAITING 调用 sleep(ms)wait(ms)join(ms)parkNanos(...) Thread.sleep(1000)Object.wait(1000)LockSupport.parkNanos(1000)
RUNNABLE TERMINATED run() 方法执行完毕或抛出未捕获异常 正常结束或异常终止
BLOCKED RUNNABLE 获取到锁(锁被释放) 线程竞争的锁被其他线程释放
WAITING RUNNABLE notify()notifyAll()unpark()interrupt() 唤醒 Object.notify()LockSupport.unpark()Thread.interrupt()
TIMED_WAITING RUNNABLE 超时、被 notify()notifyAll()unpark()interrupt() 唤醒 Object.wait(1000)Thread.sleep(1000)LockSupport.parkNanos(1000)
TERMINATED 终态,不可逆 线程结束运行

三、Java线程状态的转换详细介绍

以下是Java线程状态转换的UML图:

      +----------------+
      |     NEW        |
      +--------+-------+
               |
               | start()
               v
      +--------+----------------+
      |      RUNNABLE           |
      +--------+----------------+
               |
               | 竞争锁失败
               v
      +--------+----------------+
      |      BLOCKED            |
      +--------+----------------+
               |
               | 锁释放
               v
      +--------+----------------+
      |      RUNNABLE           |
      +--------+----------------+
               |
               | wait(), join(), park()
               v
      +--------+----------------+
      |      WAITING            |
      +--------+----------------+
               |
               | notify(), unpark(), interrupt()
               v
      +--------+----------------+
      |      RUNNABLE           |
      +--------+----------------+
               |
               | sleep(), wait(ms), join(ms), parkNanos(...)
               v
      +--------+----------------+
      |  TIMED_WAITING          |
      +--------+----------------+
               |
               | 超时/notify()/unpark()/interrupt()
               v
      +--------+----------------+
      |      RUNNABLE           |
      +--------+----------------+
               |
               | run()结束或异常
               v
      +--------+----------------+
      |    TERMINATED           |
      +-------------------------+

1. NEW → RUNNABLE

  • 触发条件:调用 start() 方法。
  • 示例
    Thread t = new Thread(() -> System.out.println("Running"));
    t.start(); // NEW → RUNNABLE
    

2. RUNNABLE ↔ BLOCKED

  • RUNNABLE → BLOCKED
    • 触发条件:线程尝试获取被其他线程持有的 synchronized 锁。
    • 示例
      Object lock = new Object();
      Thread t1 = new Thread(() -> {
          synchronized (lock) {
              // 持有锁
          }
      });
      Thread t2 = new Thread(() -> {
          synchronized (lock) {
              // 竞争失败 → BLOCKED
          }
      });
      t1.start(); // 先启动t1
      Thread.sleep(100); // 确保t1先获取锁
      t2.start(); // t2进入BLOCKED状态
      
  • BLOCKED → RUNNABLE
    • 触发条件:锁被释放(如线程执行完同步块)。
    • 示例:当 t1 释放锁后,t2 重新进入 RUNNABLE 状态。

3. RUNNABLE → WAITING

  • 触发条件:调用以下方法:
    • Object.wait()(无超时)
    • Thread.join()(无超时)
    • LockSupport.park()(无超时)
  • 示例
    Object lock = new Object();
    Thread t = new Thread(() -> {
        synchronized (lock) {
            lock.wait(); // RUNNABLE → WAITING
        }
    });
    t.start();
    

4. RUNNABLE → TIMED_WAITING

  • 触发条件:调用以下方法:
    • Thread.sleep(ms)(休眠)
    • Object.wait(ms)(带超时)
    • Thread.join(ms)(带超时)
    • LockSupport.parkNanos(...)(带超时)
  • 示例
    Thread.sleep(1000); // RUNNABLE → TIMED_WAITING
    

5. RUNNABLE → TERMINATED

  • 触发条件run() 方法执行完毕或抛出未捕获异常。
  • 示例
    Thread t = new Thread(() -> {
        System.out.println("Task done"); // 执行完毕 → TERMINATED
    });
    t.start();
    

6. WAITING → RUNNABLE

  • 触发条件:被以下方式唤醒:
    • Object.notify() / notifyAll()
    • LockSupport.unpark()
    • Thread.interrupt()
  • 示例
    Object lock = new Object();
    Thread t1 = new Thread(() -> {
        synchronized (lock) {
            lock.wait(); // 进入WAITING
        }
    });
    Thread t2 = new Thread(() -> {
        synchronized (lock) {
            lock.notify(); // 唤醒t1 → RUNNABLE
        }
    });
    t1.start();
    t2.start();
    

7. TIMED_WAITING → RUNNABLE

  • 触发条件:以下任意一种:
    • 超时:等待时间到。
    • 被唤醒notify()notifyAll()unpark()interrupt()
  • 示例
    Object lock = new Object();
    Thread t = new Thread(() -> {
        synchronized (lock) {
            lock.wait(1000); // 等待1秒后自动唤醒 → RUNNABLE
        }
    });
    t.start();
    

四、操作系统线程的生命周期

  • 操作系统中的线程生命周期通常分为以下 五种状态
    1. 新建(New)
    2. 就绪(Ready/Runnable)
    3. 运行(Running)
    4. 阻塞(Blocked/Waiting)
    5. 终止(Terminated)

【Java第70集】java线程的生命周期详解_第2张图片

1. 新建(New)

  • 状态描述
    线程被创建后,尚未开始执行。此时线程仅是一个对象实例,操作系统尚未为其分配资源(如CPU时间片、内存栈等)。
  • 典型场景
    • 在 Linux 中通过 pthread_create() 创建线程。
    • 在 Windows 中通过 CreateThread() 创建线程。
  • 状态转换
    调用 start()(如 Java)或 pthread_create() → 转换到 就绪(Ready)

2. 就绪(Ready/Runnable)

  • 状态描述
    线程已准备好执行,但尚未获得 CPU 时间片。它处于就绪队列(Ready Queue)中,等待操作系统调度器分配 CPU。
  • 特点
    • 多个线程可能同时处于就绪状态,调度策略(如时间片轮转、优先级调度)决定哪个线程先执行。
    • 在 Java 中,RUNNABLE 状态包括就绪和运行状态。
  • 状态转换
    • 获得 CPU 时间片 → 转换到 运行(Running)
    • 时间片耗尽或被抢占 → 回到 就绪(Ready)

3. 运行(Running)

  • 状态描述
    线程正在 CPU 上执行其任务(如执行 run() 方法)。
  • 特点
    • 单核 CPU 下,同一时刻只有一个线程处于运行状态;多核 CPU 可以并行运行多个线程。
    • 线程可能因主动阻塞(如调用 sleep()wait())或被动阻塞(如等待 I/O 操作)退出运行状态。
  • 状态转换
    • 调用 sleep()wait() 或等待锁 → 转换到 阻塞(Blocked)
    • 时间片耗尽 → 回到 就绪(Ready)
    • 任务完成 → 转换到 终止(Terminated)

4. 阻塞(Blocked/Waiting)

  • 状态描述
    线程因等待某种事件或资源而暂停执行。
  • 常见阻塞原因
    • 等待 I/O 操作(如读取文件、网络请求)。
    • 等待锁(如其他线程持有的锁未释放)。
    • 主动调用阻塞方法(如 sleep()wait()join())。
  • 状态转换
    • 事件完成或条件满足 → 回到 就绪(Ready)
    • 例如:
      • I/O 操作完成 → 线程恢复执行。
      • 其他线程调用 notify()/notifyAll() → 唤醒等待线程。

5. 终止(Terminated)

  • 状态描述
    线程执行完毕或因异常退出,进入终止状态。
  • 终止方式
    • 正常结束:如 returnexit()
    • 异常终止:如收到 SIGKILL 信号、抛出未捕获的异常。
  • 特点
    • 终止状态的线程不可恢复,生命周期结束。

五、Java线程与操作系统线程的关系

  • JVM层面的状态:Java线程的6种状态是JVM对线程的抽象,独立于操作系统。
  • 操作系统层面的状态:操作系统管理的线程状态通常分为:
    • 就绪(Runnable):等待CPU时间片。
    • 运行(Running):正在执行。
    • 阻塞(Blocked):等待I/O或锁。
  • 差异
    • 在JVM中,BLOCKEDWAITINGTIMED_WAITING 是导致线程无法执行的原因。
    • 在操作系统中,这些状态可能被归类为 “阻塞” 或 “休眠”。

六、实际应用中的注意事项

1. 避免死锁

  • 死锁场景:多个线程互相等待对方持有的锁。
  • 解决方案
    • 按固定顺序获取锁。
    • 使用 tryLock()ReentrantLock 提供)尝试获取锁。

2. 正确使用同步机制

  • synchronized:确保同一时间只有一个线程访问共享资源。
  • volatile:保证变量的可见性(但不提供原子性)。
  • ReentrantLock:提供比 synchronized 更灵活的锁机制。

3. 线程中断

  • interrupt():请求线程停止(需主动检查中断状态)。
  • 示例
    Thread t = new Thread(() -> {
        try {
            Thread.sleep(1000); // 进入TIMED_WAITING
        } catch (InterruptedException e) {
        	// 中断WAITING/TIMED_WAITING线程,抛出 `InterruptedException`,线程转回 `RUNNABLE`
            System.out.println("Interrupted"); 
        }
    });
    t.start();
    t.interrupt(); // 中断线程
    

4. 使用线程池管理线程

  • 线程池作用:复用线程,减少创建和销毁开销。
  • 常见实现
    • Executors.newFixedThreadPool(n):固定大小线程池。
    • Executors.newCachedThreadPool():缓存线程池。

七、总结

Java线程的生命周期由6种状态组成,每种状态对应不同的执行阶段和资源需求。理解这些状态及其转换规则是开发高效并发程序的基础。实际开发中需注意线程同步、死锁避免和资源管理,合理使用线程池和中断机制,以提升程序的性能和稳定性。

你可能感兴趣的:(Java基础,java,开发语言)