Thread 类的常用方法

方法名 说明
start() 启动新线程,JVM会调用该线程的 run() 方法。不能重复调用。
run() 线程执行体。如果直接调用,不会开启新线程,而是在当前线程中执行。
sleep(long millis) 静态方法,使当前线程休眠指定毫秒数,期间不释放锁。
sleep(long millis, int nanos) 更精确的休眠,支持纳秒。
yield() 静态方法,提示调度器当前线程愿意让出CPU,但不保证立即切换。
join() 等待该线程执行完毕。
join(long millis) 最多等待指定毫秒数,超时则继续执行。
join(long millis, int nanos) 更精确的等待时间。
interrupt() 中断线程。如果线程在 sleepwait 等阻塞状态,会抛出 InterruptedException
isInterrupted() 判断线程是否被中断,不改变中断状态
Thread.interrupted() 静态方法,判断当前线程是否被中断,并清除中断状态
isAlive() 判断线程是否处于活动状态(已启动且未终止)。
getName() / setName(String name) 获取/设置线程名。
getPriority() / setPriority(int priority) 获取/设置线程优先级(1-10)。
setDaemon(boolean on) 设置为守护线程(必须在 start() 前调用)。
currentThread() 静态方法,返回当前正在执行的线程对象。

Object 类中的线程控制方法(用于线程间通信)

这些方法必须在同步上下文(synchronized)中调用,否则会抛出 IllegalMonitorStateException

方法名 说明
wait() 当前线程释放锁并进入等待状态,直到被 notify() 或 notifyAll() 唤醒。
wait(long timeout) 等待指定时间,超时后自动唤醒。
wait(long timeout, int nanos) 更精确的等待。
notify() 唤醒在此对象监视器上等待的一个线程(随机)。
notifyAll() 唤醒在此对象监视器上等待的所有线程。

sleep与yeild方法的区别

特性 Thread.sleep(long millis) Thread.yield()
是否释放锁 ❌ 不释放任何锁(如 synchronized 锁) ❌ 不释放任何锁
是否进入阻塞状态 ✅ 是,线程进入 TIMED_WAITING 状态 ❌ 否,线程仍处于 RUNNABLE 状态
调度行为 当前线程暂停指定时间,时间到后重新竞争CPU 提示调度器“我愿意让出CPU”,但不保证会立即让出
可预测性 高,休眠时间是明确的 低,完全依赖JVM和操作系统调度器
典型用途 控制执行频率、定时任务、模拟延迟 尝试优化多线程竞争(极少使用)
Thread.sleep()
  • 作用:使当前线程暂停执行指定的毫秒数(或纳秒),在此期间不参与CPU竞争。
  • 状态变化:线程从 RUNNABLE 状态进入 TIMED_WAITING 状态。
  • 锁的持有不释放任何已持有的锁。例如,在 synchronized 块中调用 sleep(),其他线程无法进入该同步块。
  • 中断处理:如果线程在 sleep() 期间被中断(interrupt()),会抛出 InterruptedException
synchronized (lock) {
    System.out.println("Thread is about to sleep");
    Thread.sleep(2000); // 其他线程无法进入此同步块
    System.out.println("Thread woke up");
}
Thread.yield()
  • 作用:提示线程调度器当前线程愿意让出CPU,以便其他同优先级的线程有机会运行。
  • 状态变化:线程仍处于 RUNNABLE 状态,只是调度器可能会选择其他线程执行。
  • 锁的持有不释放任何锁。
  • 可预测性差yield() 的行为高度依赖JVM实现和操作系统调度策略。在某些JVM上,yield() 可能完全被忽略。
  • 典型用途:几乎不推荐使用,因为效果不确定。在现代JVM中,调度器通常比开发者更懂得如何优化线程调度。
for (int i = 0; i < 1000; i++) {
    // 高频循环,可能长时间占用CPU
    if (i % 100 == 0) {
        Thread.yield(); // 提示让出CPU,但不一定生效
    }
}

总结

// sleep() 会使线程进入 TIMED_WAITING
Thread.sleep(1000);
System.out.println(thread.getState()); // TIMED_WAITING

// yield() 后线程仍是 RUNNABLE
Thread.yield();
System.out.println(thread.getState()); // RUNNABLE
  • sleep() 是强制暂停,有明确的时间控制,线程会进入等待状态。
  • yield() 是礼貌请求,仅是一个建议,调度器可以忽略,线程仍在运行状态。

join方法实现线程间的同步

join() 方法是 Java 中 Thread 类提供的一个非常重要的方法,用于实现线程间的同步,确保一个线程等待另一个线程执行完毕后再继续执行。

join() 方法的三种形式

方法签名 说明
void join() 等待该线程无限期执行完毕。
void join(long millis) 最多等待指定的毫秒数。如果超时,主线程不再等待。
void join(long millis, int nanos) 更精确的等待时间(毫秒 + 纳秒)。

基本用法与示例

1. 基本 join():等待线程结束

Thread t1 = new Thread(() -> {
    for (int i = 0; i < 3; i++) {
        System.out.println("Thread-1: " + i);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
});

System.out.println("Main thread starts");
t1.start();
t1.join(); // 主线程等待 t1 执行完毕
System.out.println("Main thread continues after t1 finished");

输出:

Main thread starts
Thread-1: 0
Thread-1: 1
Thread-1: 2
Main thread continues after t1 finished

效果main 线程在 t1.join() 处阻塞,直到 t1 线程执行完 run() 方法后才继续。

join() 的底层原理

join() 方法的实现依赖于 wait() 和 notify() 机制:

  • 当调用 t.join() 时,当前线程(如 main)会在这个线程对象 t 上调用 wait()
  • 当线程 t 执行完毕(run() 方法结束),JVM 会自动调用 t.notifyAll() 唤醒所有等待在 t 上的线程。
  • 因此,join() 本质上是基于对象监视器的等待/通知机制。

join() 的典型应用场景

场景 说明
主线程等待子线程完成 如:主线程启动多个数据处理线程,需等待全部完成后再汇总结果。
顺序执行多个线程 实现线程 A → B → C 的串行执行。
资源清理 确保后台线程完成清理工作后再退出程序。

interrupt()打断阻塞线程和打断运行线程

调用 thread.interrupt() 并不会强制终止线程,而是:

  1. 设置线程的中断状态(interrupt status)为 true
  2. 如果线程处于阻塞状态(如 sleepwaitjoin 等),会立即抛出 InterruptedException,并清除中断状态(重置为 false)。

关键原则:中断是一种请求,线程是否响应、如何响应,由线程自身决定。

打断阻塞线程(Blocked Thread)

当一个线程正在执行可中断的阻塞方法时,调用 interrupt() 会立即中断该阻塞状态。

常见的可中断阻塞方法:

  • Thread.sleep(long millis)
  • Object.wait()
  • Thread.join()
  • BlockingQueue.put()/take()
  • Future.get()
  • Socket InputStream.read() 等

行为表现:

  1. 阻塞方法会立即抛出 InterruptedException
  2. 线程的中断状态被自动清除(变为 false)。
  3. 程序流跳转到 catch 块,可以在此处处理中断逻辑(如退出循环)。

示例:中断 sleep() 中的线程

Thread t = new Thread(() -> {
    try {
        System.out.println("Thread is about to sleep");
        Thread.sleep(5000); // 阻塞5秒
        System.out.println("Thread woke up normally");
    } catch (InterruptedException e) {
        System.out.println("Thread was interrupted during sleep!");
        System.out.println("Current interrupted status: " + Thread.currentThread().isInterrupted()); // false
        // 通常在此处退出线程
    }
});

t.start();

// 主线程等待1秒后中断 t
Thread.sleep(1000);
t.interrupt(); // 中断 t 线程
Thread is about to sleep
Thread was interrupted during sleep!
Current interrupted status: false

打断运行线程(Running Thread)

当一个线程正在执行普通代码(非阻塞)时,调用 interrupt() 只会设置其中断状态为 true不会抛出异常,线程会继续执行。

行为表现:

  1. 中断状态被设置为 true
  2. 线程继续执行,不受影响。
  3. 线程需要主动检查中断状态,并决定是否退出。

示例:中断一个正在运行的循环

Thread t = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) { // 主动检查中断状态
        System.out.println("Thread is working...");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            System.out.println("Interrupted while sleeping");
            Thread.currentThread().interrupt(); // 重要:重新设置中断状态
            break;
        }
    }
    System.out.println("Thread is exiting gracefully.");
});

t.start();
Thread.sleep(2000);
t.interrupt(); // 设置中断状态为 true
Thread is working...
Thread is working...
Thread is working...
Thread is working...
Interrupted while sleeping
Thread is exiting gracefully.

主线程与守护线程

主线程(Main Thread)

1. 什么是主线程?
  • 当一个 Java 程序启动时,JVM 会自动创建一个名为 main 的线程,即主线程
  • 它是程序的入口点,负责执行 public static void main(String[] args) 方法。
  • 所有其他线程(用户线程或守护线程)通常由主线程创建。
2. 主线程的特点
特性 说明
生命周期 属于用户线程(非守护线程)
JVM 退出影响 JVM 不会因为主线程结束而立即退出,只要还有用户线程在运行,JVM 就继续运行。
创建其他线程 主线程可以创建多个子线程(用户线程或守护线程)。
默认优先级 通常为 NORM_PRIORITY(5)
3. 示例:主线程结束,但 JVM 不退出
public class MainThreadExample {
    public static void main(String[] args) {
        Thread worker = new Thread(() -> {
            while (true) {
                System.out.println("Worker thread is running...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        });

        worker.start();

        System.out.println("Main thread is ending...");
        // 主线程结束
    }
}

输出:

Main thread is ending...
Worker thread is running...
Worker thread is running...
...

说明:即使 main 方法执行完毕,只要 worker 线程(用户线程)仍在运行,JVM 就不会退出。

守护线程(Daemon Thread)

1. 什么是守护线程?
  • 守护线程是一种后台服务线程,为其他线程提供服务(如垃圾回收、JIT 编译等)。
  • 它的存在与否不影响 JVM 的退出
  • 当 JVM 中所有用户线程结束时,无论还有多少守护线程在运行,JVM 都会自动退出
2. 如何创建守护线程?

必须在调用 start() 之前,使用 setDaemon(true) 设置:

Thread daemonThread = new Thread(() -> {
    while (true) {
        System.out.println("Daemon thread is working...");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            break;
        }
    }
});

daemonThread.setDaemon(true); // 必须在 start() 前设置
daemonThread.start();

⚠️ 注意:如果在 start() 之后调用 setDaemon(true),会抛出 IllegalThreadStateException

3. 守护线程的特点
特性 说明
用途 执行后台任务,如监控、日志、定时清理等
JVM 退出 不阻止 JVM 退出。当所有用户线程结束,JVM 强制终止所有守护线程并退出。
资源清理 不能依赖守护线程进行资源清理(如关闭文件、释放数据库连接),因为它可能随时被终止。
默认线程 JVM 自带的垃圾回收器(GC)、JIT 编译线程都是守护线程。

主线程与守护线程对比

特性 主线程(Main Thread) 守护线程(Daemon Thread)
类型 用户线程(非守护) 守护线程
创建者 JVM 自动创建 程序员手动创建并设置 setDaemon(true)
JVM 退出条件 只要有一个用户线程运行,JVM 就不退出 守护线程的存在不影响 JVM 退出
典型用途 程序入口、启动子线程 后台服务(监控、日志、定时任务)
资源清理 可以安全进行 不能依赖,可能被强制终止
设置时机 N/A 必须在 start() 之前调用 setDaemon(true)
  • 主线程是程序的起点,属于用户线程,它的结束不意味着程序结束。
  • 守护线程是后台服务线程,不阻止 JVM 退出
  • JVM 退出的唯一条件:所有用户线程都已终止。
  • 正确使用守护线程可以提升程序的后台服务能力,但需注意其不可靠性,不能用于关键任务。

线程的状态

在Java中,线程的状态(Thread State)由 java.lang.Thread.State 枚举类定义,共有 6种状态。这些状态描述了线程在其生命周期中的不同阶段。

1. NEW(新建)

  • 线程被创建,但尚未调用 start() 方法
  • 此时线程对象已经存在,但还没有被JVM调度执行。
Thread t = new Thread(() -> {});
// 此时 t.getState() == Thread.State.NEW

2. RUNNABLE(可运行)

  • 线程已经启动,正在JVM中执行,或者正在等待操作系统资源(如CPU时间片)
  • 注意:RUNNABLE 状态包括了操作系统层面的“就绪”和“运行”两种状态。
  • 并不意味着线程正在运行,只是说它可以运行,等待CPU调度。

3. BLOCKED(阻塞)

  • 线程试图获取一个对象的监视器锁(monitor lock),但该锁被其他线程持有。
  • 常见于 synchronized 代码块或方法的竞争。
  • 例如:线程A持有锁,线程B尝试进入同步代码块 → B进入 BLOCKED 状态。

4. WAITING(无限期等待)

  • 线程无限期等待另一个线程执行特定操作
  • 进入方式:
    • Object.wait()
    • Thread.join()
    • LockSupport.park()
  • 退出方式:只能由其他线程显式唤醒(如 notify()notifyAll())或被中断。

5. TIMED_WAITING(限期等待)

  • 线程在指定时间内等待,时间到后会自动恢复。
  • 进入方式:
    • Thread.sleep(long millis)
    • Object.wait(long timeout)
    • Thread.join(long millis)
    • LockSupport.parkNanos()parkUntil()
  • 不需要其他线程唤醒,超时后自动进入可运行状态。

6. TERMINATED(终止)

  • 线程的 run() 方法已经执行完毕,或者因异常退出。
  • 线程生命周期结束,无法再次启动
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐