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

Integer值未刷新:双线程wait/notify打印奇偶异常排查

问题分析与解决方案

嘿,这个问题的核心其实出在Integer的不可变性和你对变量引用的处理上,咱们一步步拆解清楚:

为什么线程看不到更新后的i?

  1. Integer是不可变类:++i这个操作看起来是修改i的值,但实际上对于Integer对象来说,它会创建一个新的Integer实例来保存递增后的值,而不是修改原来的对象本身。
  2. Runner类里的i是独立的旧引用:你在创建EvenRunner和OddRunner时,把当时的静态i(值为1的Integer对象)传给了构造函数,这两个Runner类里的i成员变量保存的是这个旧对象的引用。之后静态的volatile i虽然会指向新的Integer对象,但Runner里的i根本没跟着变,还是死死盯着原来那个值为1的对象,自然看不到更新!

你加的volatile修饰的是EvenOdd类的静态i,但Runner里的i和它已经是两个独立的引用了,volatile的可见性保障根本覆盖不到这里。

怎么修复这个问题?

有两种简单的修复方式,选哪种都可以:

方式一:直接访问静态变量

去掉EvenRunner和OddRunner里的i成员变量,直接在run方法里访问EvenOdd.i,这样每次都会取到最新的静态volatile变量引用:

修改后的EvenRunner示例:

class EvenRunner implements Runnable{
    Object lck;
    Integer N;
    EvenRunner(Object lck,Integer N){
        this.lck=lck;
        this.N=N;
    }
    @Override
    public void run() {
        while(EvenOdd.i < N) {
            synchronized(lck) {
                System.out.println(" Even Thread got lock i="+EvenOdd.i);
                while(EvenOdd.i%2==1){
                    try {
                        System.out.println(" Even Thread waiting.. i="+EvenOdd.i);
                        lck.wait();
                        System.out.println(" Even Thread woken up.. i="+EvenOdd.i);
                    }catch(InterruptedException e) {
                        System.out.println("Interrupted thread : "+e);
                    }
                }
                EvenOdd.i++;
                System.out.println(Thread.currentThread().getName()+" : i="+EvenOdd.i);
                System.out.println(" Even Thread Run called NotifyAll");
                lck.notifyAll();
            }
        }
    }
}

OddRunner做同样的修改:去掉i成员变量,所有用到i的地方替换成EvenOdd.i即可。

方式二:使用可变的数值包装类

自己写一个简单的可变int包装类,这样修改的是包装类内部的值,引用不会变,volatile可以保障可见性:

class IntHolder {
    volatile int value;
    public IntHolder(int value) {
        this.value = value;
    }
}

然后把EvenOdd里的静态变量改成static IntHolder i = new IntHolder(1);,Runner类里保存IntHolder的引用,操作的时候修改i.value:

// EvenRunner里的run方法核心片段
while(i.value < N) {
    synchronized(lck) {
        System.out.println(" Even Thread got lock i="+i.value);
        while(i.value%2==1){
            try {
                System.out.println(" Even Thread waiting.. i="+i.value);
                lck.wait();
                System.out.println(" Even Thread woken up.. i="+i.value);
            }catch(InterruptedException e) {
                System.out.println("Interrupted thread : "+e);
            }
        }
        i.value++;
        System.out.println(Thread.currentThread().getName()+" : i="+i.value);
        System.out.println(" Even Thread Run called NotifyAll");
        lck.notifyAll();
    }
}

这种方式的好处是不用依赖静态变量,代码耦合度更低。

验证修复效果

修改后,两个线程就能正确读取到彼此更新后的i值,按照奇偶顺序打印到N了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:36:39