java线程
song

线程

线程基础

Java 线程(Thread)是 Java 编程语言中实现多任务处理的基本单位,它是程序执行的最小独立路径。线程运行在 Java 虚拟机(JVM)中,允许程序并发执行多个操作。每个线程有自己的调用栈程序计数器,但与同一进程内的其他线程共享堆内存和方法区

  • 做什么
    • 实现并发执行,让程序同时处理多个任务。利用多核 CPU,提高程序响应性。
  • 解决什么问题
    • 单线程程序的响应延迟
    • 多任务处理的低效性
    • 复杂任务的耦合性
  • 如何解决问题
    • 并发:单核 CPU 上,线程通过时间分片交替执行,模拟“同时”运行。
    • 并行:多核 CPU 上,线程分配到不同核心,真正同时运行。

线程的生命周期

  • **线程的生命周期:指的是线程从创建(new Thread())到销毁(run() 方法执行完成或异常终止)的整个过程。
  • 线程状态:是线程在生命周期中某一时刻的具体状态,Java 定义了六种状态(Thread.State):NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED

线程的状态

Java 的线程状态由Thread.State枚举定义,共有六种状态,这些状态贯穿于线程从创建到销毁的整个生命周期。

  1. NEW(新建状态)
    • 线程对象已创建,但尚未调用 start() 方法。
    • 此时线程还未与底层操作系统线程关联,仅在 JVM 中分配内存。
    • Thread thread = new Thread(runnable);
  2. RUNNABLE
    • 线程已调用 start() 方法,可以被调度执行。包括“就绪”和“运行”两种子状态
      • 就绪(Ready):等待 CPU 分配时间片。
      • 运行(Running):正在执行 run() 方法。
    • 线程已分配操作系统资源(如独立的调用栈)
    • thread.start();
  3. BLOCKED(阻塞状态)
    • 线程因等待锁(如 synchronized 块或方法)而暂停执行。
    • 线程无法运行,直到锁被释放。

  4. WAITING(等待状态)
    • 线程因调用某些方法(如 wait()、join())进入无限期等待,需其他线程唤醒。
    • 线程暂停运行,等待外部通知。
    • object.wait(); 或 thread.join();
  5. TIMED_WAITING(定时等待状态)
    • 线程因调用带有超时参数的方法(如 sleep()、wait(timeout))进入有限期等待。
    • 线程会在超时后自动恢复,或被提前唤醒。
    • Thread.sleep(1000); 或 object.wait(1000);
  6. TERMINATED(终止状态)
    • 线程执行完成(run() 方法结束)或因异常终止。
    • 线程生命周期结束,资源被回收。
    • run() 方法正常返回或抛出未捕获异常。

状态之间的转换关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
flowchart TD
A[NEW]
B[RUNNABLE]
C[BLOCKED]
D[WAITING]
E[TIMED_WAITING]
F[TERMINATED]

A -->|调用 start| B
B -->|尝试进入 synchronized 区块失败| C
C -->|锁释放后获得锁| B
B -->|调用 wait/join或 park| D
D -->|收到 notify/中断唤醒| B
B -->|调用 sleep/wait/join| E
E -->|超时或被提前唤醒| B
B -->|run方法执行完毕| F

线程执行原理

  1. 线程的创建 new Thread()
    • 分配内存:JVM 在堆内存中创建 Thread 对象,并初始化其成员变量。
    • 设置线程属性:包括线程名称、优先级、是否为守护线程等。
    • 未激活状态:此时线程处于 NEW 状态,尚未与操作系统线程绑定。
      1
      2
      3
      4
      5
      6
      +-----------------+
      | Java 堆 |
      +-----------------+
      | Thread 对象 |
      | (线程对象) |
      +-----------------+
  2. 线程的启动 thread.start()
    • 在 Java 虚拟机栈中为线程分配独立的栈帧: 每个线程都有自己独立的虚拟机栈,用于存储局部变量、方法调用栈帧等信息。
    • 创建操作系统原生线程: start() 方法会调用操作系统底层的线程创建方法,创建一个与 Java 线程对应的原生线程。
    • 将线程状态设置为 RUNNABLE: 线程被创建后,初始状态为 NEW。调用 start() 方法后,线程状态变为 RUNNABLE,表示线程已经准备好运行,等待 CPU 调度。
      1
      2
      3
      4
      5
      6
      +-----------------+ +-----------------+
      | Java 堆 | | 虚拟机栈 (线程) |
      +-----------------+ +-----------------+
      | Thread 对象 | | 局部变量 |
      | (线程对象) | | 方法调用栈帧 |
      +-----------------+ +-----------------+
  3. 线程的运行 runnable.run()
    操作系统调度到该线程时,线程开始执行 Thread 对象的 run() 方法。run() 方法中包含了线程要执行的任务代码。
    • 同步机制:监视锁竞争(synchronized)、wait/notify 调用等。
    • 状态迁移触发
      • BLOCKED:竞争锁失败
      • WAITING/TIMED_WAITING:调用 Object.wait() 或 Thread.sleep()
      • TERMINATEDrun() 方法执行完成或抛出未捕获异常
        1
        2
        3
        4
        5
        6
        7
        8
        +-----------------+ +-----------------+ 
        | Java 堆 | | 虚拟机栈 (线程) |
        +-----------------+ +-----------------+
        | Thread 对象 | | 局部变量 |
        | (线程对象) | | 方法调用栈帧 |
        +-----------------+ +-----------------+
        | | | run() 方法 | <-- run() 方法应该在栈顶
        +-----------------+ +-----------------+
  4. 线程的结束
    当线程执行完 run() 方法中的所有代码后,线程会正常结束。此时,线程状态变为 TERMINATED。JVM 会回收线程的栈空间,并释放与线程相关的资源。
    1
    2
    3
    4
    5
    6
    +-----------------+ 
    | Java 堆 |
    +-----------------+
    | Thread 对象 |
    | (线程对象) |
    +-----------------+

示意图:线程生命周期与 JVM/OS 交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+----------------+       start()        +-------------------+
| NEW State | -------------------> | JVM 注册线程 |
+----------------+ | (分配栈/PC, 设置状态) |
+-------------------+
|
| 调用本地方法
v
+-------------------+
| OS 创建内核线程 |
| (pthread_create) |
+-------------------+
|
| OS 调度就绪
v
+----------------+ 获得 CPU 时间片 +-------------------+
| RUNNABLE State | <------------------ | OS 线程调度器 |
+----------------+ +-------------------+
| |
| 执行 run() | 时间片耗尽/主动让出
v |
+----------------+ +-------------------+
| RUNNING | | 线程重回就绪队列 |
| (执行用户代码) | +-------------------+
+----------------+
|
| run() 结束/异常退出
v
+----------------+
| TERMINATED | <--- JVM 清理资源 ---> OS 释放内核资源
+----------------+ GC 回收 Thread 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
flowchart TD
A["在堆中创建 Thread 对象 - \(NEW\)"]
B["调用 start() 方法 - 状态更新为 RUNNABLE"]
C["JVM内部调用 native 方法 - start0()"]
D["操作系统创建本地线程 - 分配线程栈和 CPU 上下文"]
D1["分配线程栈 - 存储局部变量和调用栈"]
D2["分配 CPU 上下文 - 程序计数器和寄存器"]
E["执行线程启动引导程序 - 初始化线程环境"]
F["进入 run() 方法 - 开始执行任务"]
G["执行线程任务 - (可能进入 sleep/wait/block 状态)"]
H["任务执行完毕 - run() 返回,进入 TERMINATED"]
I["操作系统回收线程资源 - 释放线程栈和 CPU 上下文"]
J["JVM标记 Thread 对象为垃圾 - 等待 GC 回收"]

A --> B
B --> C
C --> D
D --> D1
D --> D2
D1 & D2 --> E
E --> F
F --> G
G --> H
H --> I
I --> J

开启线程的方法

Thread

  • 继承Thread类,重写Run方法
  • 创建Thread实例,入参Runnable接口实现

继承Thread类

实现Runnable接口

实现Callable接口

线程池

Callable 与 Runnable的不同点

  • Callable支持返回值,Runnable不行
  • Callable可以抛出异常,Runnable不行
  • Callable必须和线程池联合使用
  • Runnable可在线程池和Thread类中使用

线程安全与同步机制

多个线程对同一数据(共享数据)进行读写操作,造成数据错乱

  • 同步代码块 synchronized
  • 同步方法 synchronized
  • Lock锁

死锁

线程通讯

  • 在A线程运行的过程中,可以唤醒其他处于”等待状态“的线程。
  • 需要使用到等待唤醒机制
    • 等待: wait()
    • 唤醒: notify()、notifyAll()
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      public final static Object 对象锁= new Object();

      同步代码块(对象锁){
      对象锁.wait()
      //...
      对象锁.notify() //唤醒和当前对象锁绑定的其他处于等待状态的线程
      }

      //同步方法
      非静态的同步方法() //对象锁 this
      {
      this.wait();
      //..
      this.notify();
      }


示例:生产者消费者

由 Hexo 驱动 & 主题 Keep