深入理解Java中的线程状态转换及关键同步方法
在现代软件开发中,多线程编程是实现高效、响应式应用程序的关键技术之一。理解线程的生命周期及其状态转换,对于编写健壮、性能优越的并发程序至关重要。本文将深入探讨Java中线程的各种状态转换,并详细解释join、sleep、wait、park等关键方法的作用与使用场景。
目录
- Java线程概述
 - 线程状态详解 
  
- NEW(新建状态)
 - RUNNABLE(可运行状态)
 - BLOCKED(阻塞状态)
 - WAITING(等待状态)
 - TIMED_WAITING(计时等待状态)
 - TERMINATED(终止状态)
 
 - 线程状态转换图
 - 关键同步方法解析 
  
join()方法sleep()方法wait()方法park()方法interrupt方法
 - 示例代码
 - 最佳实践与注意事项
 - 总结
 
Java线程概述
在Java中,线程是执行程序中最小的执行单元。Java通过java.lang.Thread类和java.lang.Runnable接口提供了对线程的支持。线程允许程序并行执行多个任务,从而提升应用程序的性能和响应性。
线程的生命周期由多个状态组成,理解这些状态及其转换对于掌握多线程编程至关重要。
线程状态详解
Java为线程定义了六种状态,通过java.lang.Thread.State枚举类来表示:
- NEW(新建状态)
 - RUNNABLE(可运行状态)
 - BLOCKED(阻塞状态)
 - WAITING(等待状态)
 - TIMED_WAITING(计时等待状态)
 - TERMINATED(终止状态)
 
NEW(新建状态)
- 描述:线程对象已被创建,但尚未启动。
 - 触发条件:通过
new Thread()创建一个线程对象后,线程处于NEW状态。 
Thread thread = new Thread(() -> {
    // 线程任务
});
System.out.println(thread.getState()); // 输出: NEW
 
RUNNABLE(可运行状态)
- 描述:线程已经启动,并在Java虚拟机中准备运行,或正在运行。
 - 触发条件:调用
Thread.start()方法后,线程进入RUNNABLE状态。 - 注意:
RUNNABLE状态包括正在运行(实际获得CPU时间片)和等待调度的情况。 
thread.start();
System.out.println(thread.getState()); // 输出: RUNNABLE
 
BLOCKED(阻塞状态)
- 描述:线程试图获取一个对象的监视器锁(锁竞争),无法继续执行。
 - 触发条件:线程进入
RUNNABLE状态后,试图获取一个被其他线程锁定的对象锁。 
synchronized(lock) {
    // 线程A持有锁
}
Thread threadB = new Thread(() -> {
    synchronized(lock) {
        // 线程B试图获取锁,若锁被线程A持有,则进入BLOCKED状态
    }
});
threadB.start();
 
WAITING(等待状态)
- 描述:线程无限期地等待另一个线程来执行特定操作。
 - 触发条件: 
  
- 调用
Object.wait()方法,没有指定超时时间。 - 调用
Thread.join()方法,没有指定超时时间。 - 调用
LockSupport.park()方法。 
 - 调用
 
synchronized(lock) {
    lock.wait(); // 线程进入WAITING状态
}
 
TIMED_WAITING(计时等待状态)
- 描述:线程在等待另一个线程的特定操作,且等待时间有限。
 - 触发条件: 
  
- 调用
Object.wait(long timeout)方法,指定超时时间。 - 调用
Thread.sleep(long millis)方法。 - 调用
Thread.join(long millis)方法,指定超时时间。 - 调用
LockSupport.parkNanos(long nanos)或LockSupport.parkUntil(long deadline)方法。 
 - 调用
 
Thread.sleep(1000); // 线程进入TIMED_WAITING状态
 
TERMINATED(终止状态)
- 描述:线程已经完成执行或因异常而终止。
 - 触发条件:线程的
run()方法执行完毕或因未捕获的异常导致线程终止。 
thread.join();
System.out.println(thread.getState()); // 输出: TERMINATED
 
线程状态转换图

关键同步方法解析
理解线程状态转换离不开对关键同步方法的掌握。下面详细解释join、sleep、wait、park等方法的作用及其对线程状态的影响。
join() 方法
 
详细解析
join()方法用于让一个线程等待另一个线程完成执行。它有三个重载版本:
join():无限期等待,直到目标线程终止。join(long millis):等待指定的毫秒数,或者直到目标线程终止。join(long millis, int nanos):等待指定的毫秒数和纳秒数,或者直到目标线程终止。
示例
public class JoinExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(2000); // 模拟任务
                System.out.println("子线程执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        try {
            thread.join(); // 等待子线程完成
            System.out.println("主线程继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 
输出:
子线程执行完毕
主线程继续执行
 
关键点
join()确保了线程的顺序执行。- 可以通过
join(long millis)控制等待时间,防止线程永久阻塞。 
sleep() 方法
 
详细解析
sleep()方法使当前线程暂停执行指定的时间。它有两个重载版本:
sleep(long millis):暂停指定的毫秒数。sleep(long millis, int nanos):暂停指定的毫秒数和纳秒数。
示例
public class SleepExample {
    public static void main(String[] args) {
        System.out.println("主线程开始睡眠");
        try {
            Thread.sleep(1000); // 睡眠1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程醒来");
    }
}
 
输出:
主线程开始睡眠
主线程醒来
 
关键点
sleep()不会释放锁,即使在synchronized块中调用。sleep()是静态方法,作用于当前线程。
wait() 方法
 
详细解析
wait()方法使当前线程等待,直到被其他线程调用notify()或notifyAll()方法。它有三个重载版本:
wait():无限期等待。wait(long timeout):等待指定的毫秒数。wait(long timeout, int nanos):等待指定的毫秒数和纳秒数。
示例
public class WaitNotifyExample {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("消费者等待数据...");
                    lock.wait(); // 进入WAITING状态
                    System.out.println("消费者收到通知,继续执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread producer = new Thread(() -> {
            synchronized (lock) {
                try {
                    Thread.sleep(2000); // 模拟数据生成
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产者生成数据,通知消费者");
                lock.notify(); // 唤醒消费者线程
            }
        });
        consumer.start();
        producer.start();
    }
}
 
输出:
消费者等待数据...
生产者生成数据,通知消费者
消费者收到通知,继续执行
 
关键点
wait()必须在synchronized块或方法内调用,确保线程持有对象锁。- 调用
wait()会释放对象锁,允许其他线程进入synchronized块。 - 被
notify()或notifyAll()唤醒后,线程会重新竞争锁,获取锁后继续执行。 
park() 方法
 
详细解析
park()方法是LockSupport类提供的高级线程阻塞机制,允许线程被阻塞,直到被其他线程调用unpark()或被中断。它有两种主要形式:
park():无限期等待。parkNanos(long nanos)和parkUntil(long deadline):有时间限制的等待。
示例
import java.util.concurrent.locks.LockSupport;
public class ParkUnparkExample {
    public static void main(String[] args) throws InterruptedException {
        Thread parker = new Thread(() -> {
            System.out.println("Parker 线程被阻塞");
            LockSupport.park(); // 进入 WAITING 状态
            System.out.println("Parker 线程被唤醒");
        });
        parker.start();
        Thread.sleep(1000); // 主线程等待1秒
        System.out.println("主线程调用 unpark() 唤醒 Parker 线程");
        LockSupport.unpark(parker);
    }
}
 
输出:
Parker 线程被阻塞
主线程调用 unpark() 唤醒 Parker 线程
Parker 线程被唤醒
 
关键点
park()和unpark()是配对使用的,unpark()可以在park()之前调用,导致park()不阻塞。- 不需要在
synchronized块内调用,可以灵活地用于复杂的并发控制。 park()不会抛出异常,必须通过其他机制(如中断状态)来控制线程。
interrupt 方法
 
详细解析
在Java中,interrupt机制用于中断线程的阻塞状态。通过调用线程的interrupt()方法,可以向线程发送一个中断信号,通知线程它应该停止当前的阻塞操作并尽快退出。
interrupt 的作用
 
- 中断阻塞状态的线程:当线程处于
WAITING、TIMED_WAITING或BLOCKED状态时,调用interrupt()会使线程抛出InterruptedException,从而打断阻塞状态。 - 设置中断状态:如果线程不处于阻塞状态,调用
interrupt()会设置线程的中断状态标志,但不会立即中断线程的执行。 
isInterrupted() 与 interrupted() 方法的区别
 
Java中提供了两个方法来检查线程的中断状态:isInterrupted()和interrupted()。它们的区别如下:
-  
isInterrupted()方法- 用途:检查线程是否被中断。
 - 行为:不会清除中断状态。
 - 使用场景:适用于需要多次检查中断状态的情况。
 - 示例:
 
Thread thread = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { // 执行任务 } System.out.println("线程被中断,退出循环"); }); thread.start(); thread.interrupt(); -  
interrupted()方法- 用途:检查当前线程是否被中断。
 - 行为:会清除当前线程的中断状态。
 - 使用场景:适用于只需要一次性检查中断状态的情况。
 - 示例:
 
Thread thread = new Thread(() -> { while (!Thread.interrupted()) { // 执行任务 } System.out.println("线程被中断,退出循环"); }); thread.start(); thread.interrupt(); 
示例
public class InterruptExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("线程开始睡眠");
                Thread.sleep(5000); // 进入 TIMED_WAITING 状态
            } catch (InterruptedException e) {
                System.out.println("线程被中断,捕获 InterruptedException");
            }
            // 检查中断状态
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("线程中断状态为 true");
            } else {
                System.out.println("线程中断状态为 false");
            }
            // 使用 interrupted() 方法
            boolean interrupted = Thread.interrupted();
            System.out.println("调用 interrupted() 后,中断状态为: " + interrupted);
        });
        thread.start();
        try {
            Thread.sleep(1000); // 主线程等待1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程调用 interrupt() 中断子线程");
        thread.interrupt();
    }
}
 
输出:
线程开始睡眠
主线程调用 interrupt() 中断子线程
线程被中断,捕获 InterruptedException
线程中断状态为 false
调用 interrupted() 后,中断状态为: false
 
解释:
- 子线程开始执行并进入睡眠状态,进入
TIMED_WAITING。 - 主线程等待1秒后调用
interrupt()中断子线程。 - 子线程在睡眠期间被中断,抛出
InterruptedException并捕获。 - 捕获异常后,子线程检查中断状态,发现已经被清除(
isInterrupted()返回false)。 - 调用
Thread.interrupted()再次检查中断状态,返回false,并清除中断状态标志。 
关键点
isInterrupted():- 检查线程的中断状态,不会清除状态。
 - 适用于需要多次检查的场景。
 
interrupted():- 检查当前线程的中断状态,并清除状态。
 - 适用于一次性检查的场景。
 
方法对比总结
| 方法 | 所属类 | 用途 | 阻塞状态 | 释放锁 | 唤醒方式 | 其他特点 | 
|---|---|---|---|---|---|---|
join() | java.lang.Thread | 等待另一个线程完成执行 | WAITING / TIMED_WAITING | 无 | 目标线程终止或超时 | 用于线程顺序控制,主线程等待子线程 | 
sleep() | java.lang.Thread | 暂停当前线程执行指定时间 | TIMED_WAITING | 无 | 睡眠时间到达或被中断 | 不依赖于任何锁,常用于定时任务 | 
wait() | java.lang.Object | 等待其他线程的通知 | WAITING / TIMED_WAITING | 是 | notify() 或 notifyAll() | 必须在synchronized块内调用,释放锁 | 
park() | java.util.concurrent.locks.LockSupport | 阻塞当前线程,直到被unpark唤醒 | WAITING / TIMED_WAITING | 无 | unpark() 或被中断 | 更灵活,非synchronized,可用于高级并发工具 | 
interrupt() | java.lang.Thread | 中断线程的阻塞状态或设置中断标志 | 触发阻塞线程抛出InterruptedException | 无 | interrupt()调用者 | 可以用于打断阻塞状态,设置中断标志 | 
详细对比
-  
阻塞条件与唤醒方式:
join():依赖于另一个线程的终止状态或超时自动唤醒。sleep():基于时间自动唤醒,不依赖于任何外部事件。wait():依赖于其他线程的notify()或notifyAll()调用。park():依赖于其他线程的unpark()调用或线程被中断。interrupt():通过调用interrupt()方法可以中断正在阻塞的线程,如wait()、sleep()、park()等。
 -  
锁的释放:
wait():在调用时会释放对象锁,允许其他线程进入synchronized块。join()、sleep()、park()、interrupt():不释放任何锁。
 -  
调用环境:
wait():必须在synchronized块或方法内调用。join()、sleep()、park()、interrupt():不需要在synchronized块内调用(除了wait()必须在同步环境中)。
 -  
灵活性与适用场景:
join():适用于线程间顺序执行的场景。sleep():适用于需要暂停线程执行的场景,如定时任务或延时操作。wait():适用于线程间需要等待特定条件的场景,如生产者-消费者模型。park():适用于需要更灵活控制线程阻塞与唤醒的高级并发场景,通常在构建并发框架时使用。interrupt():适用于需要中断阻塞线程或设置中断标志的场景,用于控制线程的执行流程。
 
示例代码
以下示例代码展示了join、sleep、wait、park和interrupt方法的区别与使用。
public class SyncMethodsComparison {
    private static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        // 示例1:join()
        Thread thread1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("Thread1 完成执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread1.start();
        thread1.join(); // 主线程等待 thread1 完成
        System.out.println("主线程在 join() 后继续执行");
        // 示例2:sleep()
        Thread thread2 = new Thread(() -> {
            try {
                System.out.println("Thread2 开始睡眠");
                Thread.sleep(2000);
                System.out.println("Thread2 睡眠结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread2.start();
        thread2.join(); // 等待 thread2 完成
        System.out.println("主线程在 sleep() 后继续执行");
        // 示例3:wait() 和 notify()
        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("消费者等待数据...");
                    lock.wait(); // 进入 WAITING 状态
                    System.out.println("消费者收到通知,继续执行");
                } catch (InterruptedException e) {
                    System.out.println("消费者被中断");
                }
            }
        });
        Thread producer = new Thread(() -> {
            try {
                Thread.sleep(1000); // 模拟数据生成
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock) {
                System.out.println("生产者生成数据,通知消费者");
                lock.notify(); // 唤醒消费者线程
            }
        });
        consumer.start();
        producer.start();
        consumer.join();
        producer.join();
        System.out.println("主线程在 wait()/notify() 后继续执行");
        // 示例4:park() 和 unpark()
        Thread parker = new Thread(() -> {
            System.out.println("Parker 线程被阻塞");
            LockSupport.park(); // 进入 WAITING 状态
            System.out.println("Parker 线程被唤醒");
        });
        parker.start();
        Thread.sleep(1000); // 主线程等待1秒
        System.out.println("主线程调用 unpark() 唤醒 Parker 线程");
        LockSupport.unpark(parker);
        parker.join();
        System.out.println("主线程在 park()/unpark() 后继续执行");
        // 示例5:interrupt() 方法
        Thread interrupter = new Thread(() -> {
            try {
                System.out.println("Interrupter 线程开始睡眠");
                Thread.sleep(5000); // 进入 TIMED_WAITING 状态
            } catch (InterruptedException e) {
                System.out.println("Interrupter 线程被中断,捕获 InterruptedException");
            }
            // 检查中断状态
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupter 线程中断状态为 true");
            } else {
                System.out.println("Interrupter 线程中断状态为 false");
            }
            // 使用 interrupted() 方法
            boolean interrupted = Thread.interrupted();
            System.out.println("调用 interrupted() 后,中断状态为: " + interrupted);
        });
        interrupter.start();
        Thread.sleep(1000); // 主线程等待1秒
        System.out.println("主线程调用 interrupt() 中断 interrupter 线程");
        interrupter.interrupt();
        interrupter.join();
        System.out.println("主线程在 interrupt() 后继续执行");
    }
}
 
输出:
Thread1 完成执行
主线程在 join() 后继续执行
Thread2 开始睡眠
Thread2 睡眠结束
主线程在 sleep() 后继续执行
消费者等待数据...
生产者生成数据,通知消费者
消费者收到通知,继续执行
主线程在 wait()/notify() 后继续执行
Parker 线程被阻塞
主线程调用 unpark() 唤醒 Parker 线程
Parker 线程被唤醒
主线程在 park()/unpark() 后继续执行
Interrupter 线程开始睡眠
主线程调用 interrupt() 中断 interrupter 线程
Interrupter 线程被中断,捕获 InterruptedException
Interrupter 线程中断状态为 false
调用 interrupted() 后,中断状态为: false
主线程在 interrupt() 后继续执行
 
解释:
-  
join()示例:- 主线程启动
thread1并调用join(),主线程等待thread1完成后继续执行。 
 - 主线程启动
 -  
sleep()示例:- 主线程启动
thread2,thread2进入睡眠2秒,醒来后完成执行。 - 主线程通过
join()等待thread2完成,然后继续执行。 
 - 主线程启动
 -  
wait()和notify()示例:consumer线程进入synchronized块并调用wait(),进入WAITING状态。producer线程等待1秒后进入synchronized块,调用notify()唤醒consumer线程。consumer线程被唤醒后继续执行。
 -  
park()和unpark()示例:parker线程调用LockSupport.park()进入WAITING状态。- 主线程等待1秒后调用
LockSupport.unpark(parker)唤醒parker线程。 parker线程被唤醒后继续执行。
 -  
interrupt()方法示例:interrupter线程开始睡眠5秒,进入TIMED_WAITING状态。- 主线程等待1秒后调用
interrupter.interrupt()中断interrupter线程。 interrupter线程在睡眠期间被中断,抛出InterruptedException并捕获。- 捕获异常后,
interrupter线程检查中断状态,发现已被清除(isInterrupted()返回false)。 - 调用
Thread.interrupted()再次检查中断状态,返回false,并清除中断状态标志。 
 
最佳实践与注意事项
-  
避免死锁:
- 确保多个线程获取锁的顺序一致。
 - 尽量减少锁的持有时间。
 - 使用超时机制(如
tryLock)防止长时间等待锁。 
 -  
合理使用
synchronized:- 只在必要的代码块使用
synchronized,避免过度同步导致性能下降。 - 优先使用更高级的并发工具,如
ReentrantLock,以获得更灵活的锁控制。 
 - 只在必要的代码块使用
 -  
理解
wait()与notify()的配合使用:wait()和notify()必须在synchronized块内调用,确保线程持有对象锁。- 使用
while循环检查条件,以防止虚假唤醒。 
synchronized (lock) { while (!condition) { lock.wait(); } // 继续执行 } -  
合理使用
park()与unpark():LockSupport提供了更灵活的线程阻塞机制,但需要谨慎使用,避免线程永远被阻塞。
 -  
使用
join()确保线程执行顺序:- 在需要等待某个线程完成后再继续执行的场景下,使用
join()方法确保执行顺序。 
 - 在需要等待某个线程完成后再继续执行的场景下,使用
 -  
避免使用过期的并发工具:
- 尽量使用Java提供的高级并发工具(如
CountDownLatch、Semaphore、CyclicBarrier等)代替低级别的synchronized和wait/notify机制,以提高代码的可读性和可维护性。 
CountDownLatch latch = new CountDownLatch(1); Thread worker = new Thread(() -> { // 处理任务 latch.countDown(); // 任务完成,减少计数 }); worker.start(); // 等待任务完成 latch.await(); - 尽量使用Java提供的高级并发工具(如
 -  
处理中断:
- 在可能阻塞的方法(如
wait()、sleep()、park())中,正确处理中断信号,确保线程能够及时响应中断。 - 避免忽略
InterruptedException,应根据业务需求决定如何处理中断。 
try { Thread.sleep(1000); } catch (InterruptedException e) { // 处理中断,如清理资源、退出线程等 Thread.currentThread().interrupt(); // 重新设置中断状态 } - 在可能阻塞的方法(如
 
总结
理解Java中线程的状态及其转换机制,是掌握多线程编程的基础。通过熟悉NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED等线程状态,以及join、sleep、wait、park和interrupt等关键方法的作用与使用场景,开发者可以更高效地设计和实现并发程序。
正确理解和使用这些同步方法,能够帮助开发者更好地控制线程行为,避免常见的并发问题,如死锁、竞态条件等。同时,结合Java提供的高级并发工具,可以编写出更高效、可维护的多线程程序。
希望本文能够帮助你深入理解Java中的线程状态转换及关键同步方法,并在实际开发中灵活应用。如有任何问题或需要进一步探讨,欢迎在评论区留言交流!







![[产品管理-17]:NPDP新产品开发 - 15 - 产品设计与开发工具 - 工欲善其事,必先利其器 - 创意工具:借助各种工具和方法,完成产品的创意](https://i-blog.csdnimg.cn/direct/2f12c479713a494daa73600b4067215f.png)










