You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

核心Java中如何让长时间等待的线程运行?线程顺序打印问题求助

解决多线程依次打印1、2、3时的线程等待问题

咱们一步步拆解你代码里的问题,然后给出完整的修复方案:

原代码的核心问题

  1. 条件判断用if而非while:线程被notify()唤醒后可能遇到「虚假唤醒」(系统层面的意外唤醒),如果只用if判断,会直接跳过检查执行后续逻辑,导致顺序混乱或者线程卡死。
  2. 未处理不满足条件的情况:比如Thread2拿到锁后,发现当前数字不是1,但代码里没有让它wait()释放锁,要么会一直循环占用锁,要么进入错误的逻辑。
  3. notify()的局限性:notify()只会随机唤醒一个等待的线程,如果唤醒的不是下一个该执行的线程,就会导致那个线程再次wait(),而正确的线程没被唤醒,最终所有线程都陷入等待。
  4. 缺少Thread3的完整逻辑:原代码里完全没处理Thread3的分支,它拿到锁后根本不知道该做什么。

修复后的完整代码

首先定义共享的锁对象和计数器:

import java.util.concurrent.atomic.AtomicInteger;

public class OrderedPrinting {
    private static final Object lock = new Object();
    private static final AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) {
        Thread thread1 = new Thread(new PrintTask(0), "Thread1");
        Thread thread2 = new Thread(new PrintTask(1), "Thread2");
        Thread thread3 = new Thread(new PrintTask(2), "Thread3");

        thread1.start();
        thread2.start();
        thread3.start();
    }

    static class PrintTask implements Runnable {
        private final int targetState;

        public PrintTask(int targetState) {
            this.targetState = targetState;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    // 用while循环检查条件,处理虚假唤醒
                    while (counter.get() != targetState) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            // 线程被中断时,正确退出
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }

                    // 打印当前数字和线程名
                    int num = counter.incrementAndGet();
                    System.out.print(num + "(" + Thread.currentThread().getName() + ") ");

                    // 唤醒所有等待的线程,让它们重新检查条件
                    lock.notifyAll();

                    // 打印完3后重置计数器,实现循环打印
                    if (num == 3) {
                        counter.set(0);
                        System.out.println(); // 换行分隔每次循环
                        try {
                            // 可选:放慢速度,方便观察效果
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                }
            }
        }
    }
}

关键修复点说明

  • 用while替代if判断条件:每次线程被唤醒后,都会重新检查当前计数器是否符合自己的执行条件,彻底避免虚假唤醒带来的问题。
  • 不满足条件时调用wait():释放锁,让其他线程有机会获取锁并执行,不会出现某线程一直占用锁的情况。
  • 使用notifyAll()唤醒所有线程:确保所有等待的线程都能被唤醒,然后各自检查条件,找到下一个该执行的线程,不会出现唤醒错误线程导致全部卡死的情况。
  • 统一的任务封装:把打印逻辑封装成PrintTask,通过构造参数传入每个线程对应的目标状态,代码更简洁易维护。
  • 处理线程中断:当线程被中断时,正确退出并标记中断状态,避免资源泄漏。

这样修改后,三个线程就会严格按照Thread1→Thread2→Thread3的顺序依次打印1、2、3,循环往复,不会出现线程长时间等待无法运行的问题。

内容的提问来源于stack exchange,提问作者Vaithilingam K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:07:05