Skip to content

线程

线程状态转换

线程在生命周期中会经历以下六种状态:

  • 新建 (New): 线程被创建,但尚未启动 (还未调用 start() 方法)。
  • 可运行 (Runnable): 线程可以被 CPU 调度执行。可能正在运行 (Running),也可能在等待 CPU 时间片 (Ready)。
  • 阻塞 (Blocking): 线程等待获取排它锁,例如等待 synchronized 关键字或 Lock 锁。当线程获得锁后,会从阻塞状态解除。
  • 无限期等待 (Waiting): 线程进入等待状态,必须等待其他线程显式地唤醒。
    • 进入方法:
      • 不带 timeout 参数的 Object.wait()
      • 不带 timeout 参数的 Thread.join()
      • LockSupport.park()
    • 退出方法:
      • Object.notify() / Object.notifyAll() (唤醒等待 Object 锁的线程)
      • join() 的线程执行完毕
      • LockSupport.unpark() (唤醒被 park() 挂起的线程)
  • 限期等待 (Timed Waiting): 线程等待一段时间后,无需其他线程显式唤醒,系统会自动唤醒。
    • 进入方法:
      • Thread.sleep(timeout)
      • 带 timeout 参数的 Object.wait(timeout)
      • 带 timeout 参数的 Thread.join(timeout)
      • LockSupport.parkNanos(timeout)
      • LockSupport.parkUntil(deadline)
    • 退出方法:
      • 等待时间结束
      • Object.notify() / Object.notifyAll()
      • join() 的线程执行完毕
      • LockSupport.unpark()
  • 死亡 (Terminated): 线程执行完成或因异常终止。

状态对比:

  • 阻塞 vs. 等待: 阻塞是被动等待,等待是主动等待唤醒超时
  • 睡眠 vs. 挂起: 睡眠 (sleep()) 和 挂起 (wait()) 都是使线程暂停执行的行为,阻塞和等待是描述线程状态。

线程使用方式

Java 中创建线程的三种方式:

  1. 实现 Runnable 接口: 将任务逻辑放在 run() 方法中。

    ``` public class MyRunnable implements Runnable { public void run() { // 任务逻辑 } }

    MyRunnable instance = new MyRunnable(); Thread thread = new Thread(instance); thread.start(); ```

  2. 实现 Callable 接口:Runnable 类似,但 call() 方法可以有返回值,通过 FutureTask 获取。

    ``` public class MyCallable implements Callable { public Integer call() { return 123; } }

    MyCallable mc = new MyCallable(); FutureTask ft = new FutureTask<>(mc); Thread thread = new Thread(ft); thread.start(); int result = ft.get(); // 获取返回值 ```

  3. 继承 Thread 类: 继承 Thread 类并重写 run() 方法。

    ``` public class MyThread extends Thread { public void run() { // 任务逻辑 } }

    MyThread mt = new MyThread(); mt.start(); ```

选择 Runnable/Callable 接口 vs. 继承 Thread: 推荐实现接口,更灵活,避免单继承限制,降低类耦合性。

基础线程机制

  • Executor 框架: 管理线程池,简化线程生命周期管理,异步执行任务。

    • CachedThreadPool: 按需创建线程,线程可重用。
    • FixedThreadPool: 固定大小线程池,控制并发数。
    • SingleThreadExecutor: 单线程池,顺序执行任务。

    ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new MyRunnable()); // 提交 Runnable 任务 executorService.shutdown(); // 关闭线程池

  • 守护线程 (Daemon Thread): 后台服务线程,程序结束时守护线程会被终止。main() 线程是非守护线程。使用 thread.setDaemon(true) 设置。

  • sleep(): 线程休眠指定毫秒数,让出 CPU,不释放锁。可能抛出 InterruptedException 异常 (必须处理)。

  • yield(): 建议线程调度器让当前线程让出 CPU 给同优先级线程,仅为建议,不保证生效。

线程中断

  • interrupt() 方法: 中断线程,设置中断标志。
    • 如果线程处于 阻塞/限期等待/无限期等待 状态,抛出 InterruptedException 异常,提前结束线程。
    • 无法中断 I/O 阻塞和 synchronized 锁阻塞。
  • interrupted() 方法: 检查线程是否被中断 (中断标志是否被设置)。常用于循环中判断是否需要结束线程。

  • Executor 的中断:

    • shutdown(): 平滑关闭,等待所有任务执行完成。
    • shutdownNow(): 立即关闭,尝试中断所有正在执行的线程 (interrupt())。
    • Future.cancel(true): 中断 Executor单个线程。

线程互斥同步

Java 提供 synchronized 关键字和 ReentrantLock 类实现线程互斥同步。

  • synchronized 关键字: JVM 实现,隐式锁,自动释放锁。

    1. 同步代码块: synchronized (this | object),锁定对象实例或指定对象。

      public void func1() { synchronized (this) { // 锁当前对象实例 // 同步代码 } }

    2. 同步方法: public synchronized void func () {},锁定对象实例。

    3. 同步类: synchronized (SynchronizedExample.class),锁定整个类。

      Java

      public void func2() { synchronized (SynchronizedExample.class) { // 锁类对象 // 同步代码 } }

    4. 同步静态方法: public synchronized static void fun() {},锁定整个类。

  • ReentrantLock 类: JDK 实现,显式锁,需手动 lock()unlock() (通常在 finally 块中释放,避免死锁)。

    Java

    ``` private Lock lock = new ReentrantLock();

    public void func() { lock.lock(); try { // 同步代码 } finally { lock.unlock(); } } ```

synchronized vs. ReentrantLock:

特性 synchronized ReentrantLock
实现 JVM JDK
性能 JDK 优化后相近 JDK 优化后相近
等待可中断
公平锁 非公平 默认非公平,可设为公平
锁绑定条件数 1 多个 Condition 对象
锁释放 自动 手动 (unlock())

选择建议: 优先使用 synchronized,除非需要 ReentrantLock 的高级特性 (如公平锁、可中断、多条件)。

线程之间的协作

线程协作解决多线程任务间的执行顺序和数据共享问题。

  • join() 方法: 线程 A 中调用线程 B 的 join(),线程 A 进入等待状态,直到线程 B 结束才继续执行。保证线程执行顺序。

    A a = new A(); B b = new B(a); b.start(); // B 线程启动 a.start(); // A 线程启动 (但 B 中 join(a) 会等待 A 先执行完)

  • wait(), notify(), notifyAll() 方法: 对象监视器方法,用于线程间条件等待唤醒必须在 synchronized 代码块或方法中使用wait() 释放锁,sleep() 不释放锁。

    ``` public synchronized void before() { notifyAll(); // 唤醒等待线程 }

    public synchronized void after() { wait(); // 等待被唤醒 } ```

  • await(), signal(), signalAll() 方法: Condition 对象的方法,提供更灵活的条件等待唤醒机制,需配合 ReentrantLock 使用。

    ``` private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition();

    public void before() { lock.lock(); condition.signalAll(); // 唤醒等待线程 lock.unlock(); }

    public void after() { lock.lock(); condition.await(); // 等待被唤醒 lock.unlock(); } ```

wait() vs. sleep():

特性 wait() sleep()
方法归属 Object Thread
锁释放 释放锁 不释放锁
使用场景 线程间协作 线程休眠

线程的状态及转换

Java线程有6种状态:

  1. NEW(新建)

    • 创建线程对象后,尚未调用start()方法
    • 转换:调用start()→RUNNABLE
    • RUNNABLE(可运行)

    • 线程已启动,正在执行或等待CPU资源

    • 转换:
      • 获取锁失败→BLOCKED
      • 调用wait()/join()/LockSupport.park()→WAITING
      • 调用sleep(time)/wait(time)→TIMED_WAITING
      • 任务执行完毕→TERMINATED
    • BLOCKED(阻塞)

    • 线程等待获取监视器锁进入同步区域

    • 转换:获取到锁→RUNNABLE
    • WAITING(等待)

    • 无限期等待其他线程执行特定操作

    • 转换:
      • 对于wait()notify()/notifyAll()→RUNNABLE/BLOCKED
      • 对于join():目标线程结束→RUNNABLE
      • 对于LockSupport.park()unpark()→RUNNABLE
    • TIMED_WAITING(限时等待)

    • 有限时间的等待状态

    • 转换:
      • 等待时间结束→RUNNABLE
      • 提前收到通知→RUNNABLE
    • TERMINATED(终止)

    • 线程完成执行

线程的使用方式

  1. 继承Thread类

    Java

    class MyThread extends Thread { public void run() { /* 代码 */ } } // 使用:new MyThread().start();

  2. 实现Runnable接口(推荐)

    Java

    class MyTask implements Runnable { public void run() { /* 代码 */ } } // 使用:new Thread(new MyTask()).start();

  3. 实现Callable接口(可返回结果)

    Java

    class MyCallable implements Callable<String> { public String call() throws Exception { return result; } } // 使用: FutureTask<String> task = new FutureTask<>(new MyCallable()); new Thread(task).start(); String result = task.get(); // 获取结果,会阻塞

  4. 使用线程池

    Java

    ExecutorService executor = Executors.newFixedThreadPool(10); executor.submit(() -> { /* 任务代码 */ });

  5. 使用CompletableFuture(Java 8+)

    Java

    CompletableFuture.supplyAsync(() -> /* 任务代码 */) .thenAccept(result -> /* 处理结果 */);

基础线程机制

  1. 线程池

    • ThreadPoolExecutor:自定义线程池
    • Executors工厂类:提供常用线程池实现
    • 线程控制

    • sleep():线程休眠指定时间,不释放锁

    • yield():提示线程调度器让出CPU执行权
    • join():等待目标线程执行完成
    • 线程优先级

    • setPriority(int):设置优先级(1-10),仅为建议性

    • 守护线程

    • setDaemon(true):设置为守护线程,JVM不会等待守护线程结束

线程的中断方式

  1. 通过中断标志

    • thread.interrupt():设置中断标志
    • thread.isInterrupted():检查中断状态(不清除)
    • Thread.interrupted():检查当前线程中断状态(会清除)
    • 通过共享变量

    Java

    private volatile boolean running = true; public void run() { while (running) { // 任务代码 } } // 中断:running = false;

  2. 通过Future取消

    Java

    Future<?> future = executor.submit(task); future.cancel(true); // 尝试中断正在执行的任务

线程的互斥同步方式

  1. synchronized关键字

    • 特点:Java内置锁,自动获取/释放

    synchronized (object) { /* 同步块 */ } public synchronized void method() { /* 同步方法 */ }

  2. ReentrantLock

    • 特点:显式锁,更灵活,需手动释放

    Lock lock = new ReentrantLock(); lock.lock(); try { // 临界区代码 } finally { lock.unlock(); // 必须在finally中释放锁 }

  3. ReadWriteLock

    • 特点:读写分离,适合读多写少场景

    ReadWriteLock rwLock = new ReentrantReadWriteLock(); // 读操作:多线程可同时获取 rwLock.readLock().lock(); // 写操作:独占锁 rwLock.writeLock().lock();

  4. StampedLock(Java 8+)

    • 特点:支持乐观读,性能更好

如何选择?

  • 简单场景:使用synchronized(代码简洁,JVM优化)
  • 需要高级特性:使用ReentrantLock(可中断、限时、公平性)
  • 读多写少:使用ReadWriteLockStampedLock

线程之间的协作方式

  1. wait/notify机制

    ``` synchronized (obj) { while (!condition) { obj.wait(); // 释放锁并等待 } // 条件满足,继续执行 }

    // 另一个线程 synchronized (obj) { // 改变条件 obj.notify(); // 或 obj.notifyAll(); } ```

  2. Condition接口

    ``` Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();

    lock.lock(); try { while (!conditionMet) { condition.await(); } } finally { lock.unlock(); }

    // 另一个线程 lock.lock(); try { // 改变条件 condition.signal(); // 或 condition.signalAll(); } finally { lock.unlock(); } ```

  3. CountDownLatch(一次性同步点)

    ``` CountDownLatch latch = new CountDownLatch(3); // 等待线程 latch.await(); // 等待计数器归零

    // 工作线程 latch.countDown(); // 完成工作,计数器减1 ```

  4. CyclicBarrier(可重用同步点)

    CyclicBarrier barrier = new CyclicBarrier(3); // 每个线程 barrier.await(); // 等待所有线程到达

  5. Phaser(灵活的多阶段同步)

  6. Semaphore(控制并发数)

    Semaphore semaphore = new Semaphore(5); semaphore.acquire(); // 获取许可 try { // 访问受限资源 } finally { semaphore.release(); // 释放许可 }

  7. Exchanger(线程间交换数据)

  8. 阻塞队列(生产者-消费者模式)

    BlockingQueue<T> queue = new LinkedBlockingQueue<>(); // 生产者线程 queue.put(item); // 可能阻塞 // 消费者线程 T item = queue.take(); // 可能阻塞

通过合理选择和组合这些机制,可以有效解决不同场景下的线程协作问题。