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

Java简单同步问题:为何线程测试未出现预期交错结果?

为什么你的多线程Counter测试始终输出0?

这是个很常见的多线程入门坑,核心原因是你的测试代码里每个线程只执行了一次操作,竞态条件出现的概率低到几乎可以忽略,所以你看不到非预期的结果。具体拆解下:

  • 单次操作的调度特性:虽然c++和c--本质是「读取-修改-写入」三个非原子步骤,但当每个线程只做一次的时候,操作系统的线程调度很难刚好卡在这三个步骤之间切换线程。大部分情况下,线程t1会完整跑完increment(),再轮到t2执行decrement(),或者反过来,最终结果自然是0。
  • 隐式同步的干扰:你在run()里加的System.out.println(counter.value())其实自带了隐式同步(System.out的println方法是加了锁的),这反而进一步降低了线程交错的概率,让结果更稳定。

怎么复现竞态条件?

要看到非预期的错误结果,你需要让线程重复执行多次操作,比如循环10000次,这样线程调度的交错概率会大大提升。修改后的测试代码如下:

public class TestPara {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        AdderCounter ac = new AdderCounter(counter);
        SubberCounter sc = new SubberCounter(counter);
        Thread t1 = new Thread(ac);
        Thread t2 = new Thread(sc);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("最终结果: " + counter.c);
    }
}

class AdderCounter implements Runnable {
    Counter counter;
    public AdderCounter(Counter counter) {
        this.counter = counter;
    }
    @Override
    public void run() {
        // 循环执行10000次递增
        for (int i = 0; i < 10000; i++) {
            counter.increment();
        }
    }
}

class SubberCounter implements Runnable {
    Counter counter;
    public SubberCounter(Counter counter) {
        this.counter = counter;
    }
    @Override
    public void run() {
        // 循环执行10000次递减
        for (int i = 0; i < 10000; i++) {
            counter.decrement();
        }
    }
}

class Counter {
    int c = 0;
    public void increment() { c++; }
    public void decrement() { c--; }
    public int value() { return c; }
}

运行这段代码,你会发现最终结果大概率不是0,而是一个随机的负数或正数——这就是竞态条件导致的错误。

怎么解决这个问题?

按照Oracle文档的建议,你可以通过两种方式修复:

  • 给Counter的方法添加synchronized修饰,保证同一时间只有一个线程能执行这些方法:
class Counter {
    int c = 0;
    public synchronized void increment() { c++; }
    public synchronized void decrement() { c--; }
    public synchronized int value() { return c; }
}
  • 使用java.util.concurrent.atomic.AtomicInteger替代普通int,它本身就是线程安全的原子类,无需手动加锁:
import java.util.concurrent.atomic.AtomicInteger;

class Counter {
    AtomicInteger c = new AtomicInteger(0);
    public void increment() { c.incrementAndGet(); }
    public void decrement() { c.decrementAndGet(); }
    public int value() { return c.get(); }
}

修改后再运行测试代码,最终结果就会稳定为0了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:02:57