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

为什么我的synchronized方法未生效?同步与无锁线程执行差异验证

嘿,我来帮你理清楚synchronized关键字和无锁情况下线程执行的差异~

一、先把你的代码格式化好
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class mainClass {
    static int count = 0;

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Runnable r = new Runnable() {
            public synchronized void run() {
                count = count + 1;
                // 推测你这里应该还有后续逻辑,比如循环提交任务、关闭线程池等
            }
        };
        // 假设你是循环提交任务来验证,比如执行1000次自增
        IntStream.range(0, 1000).forEach(i -> executor.submit(r));
        executor.shutdown();
        // 等待线程池执行完毕后打印结果
        while (!executor.isTerminated()) {}
        System.out.println("最终count值: " + count);
    }
}
二、核心差异分析

1. 你的当前代码(带synchronized)的效果

因为你只创建了一个Runnable实例r,所以线程池里的两个线程调用synchronized run()时,会争抢这个r对象的内置锁(monitor lock)。同一时间只有一个线程能进入run方法执行count +=1,这就保证了自增操作的原子性,最终count的结果会是准确的1000。

2. 去掉synchronized的情况

如果把run方法的synchronized关键字去掉,代码变成:

Runnable r = new Runnable() {
    public void run() {
        count = count + 1;
    }
};

这时候count +=1不是原子操作,它拆分成了三步:

  • 读取当前count的数值
  • 对数值执行加1操作
  • 将新值写回count

当两个线程同时执行这三步时,就会出现竞态条件:比如线程A读取count=5,线程B也读取count=5,两者都加1得到6,再写回count,最终count只增加了1,但实际上两个线程都执行了自增。最终运行结果会小于1000,而且每次运行的结果都可能不一样(比如985、993等)。

3. 容易踩的坑:锁错对象

如果你创建多个Runnable实例,比如:

IntStream.range(0, 1000).forEach(i -> executor.submit(new Runnable() {
    public synchronized void run() {
        count +=1;
    }
}));

这时候每个Runnable实例都有自己的锁,线程之间不会互相阻塞,同样会出现竞态条件,count结果还是不准确。这种情况应该锁共享的对象,比如类对象或者专门定义的锁:

// 方式1:锁类对象
private static void increment() {
    synchronized (mainClass.class) {
        count +=1;
    }
}

// 方式2:用专门的共享锁对象
private static final Object lock = new Object();
private static void increment() {
    synchronized (lock) {
        count +=1;
    }
}
三、直观验证差异的完整示例

为了更明显看到差异,你可以运行下面两个版本的代码:

版本1:无锁版本

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class NoLockDemo {
    static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10); // 用更多线程放大差异
        IntStream.range(0, 10000).forEach(i -> executor.submit(() -> count++));
        executor.shutdown();
        while (!executor.isTerminated()) {}
        System.out.println("无锁情况下的count: " + count);
    }
}

运行结果通常会小于10000,比如9940左右,每次结果都不固定。

版本2:正确加锁的版本

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class SynchronizedDemo {
    static int count = 0;
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        IntStream.range(0, 10000).forEach(i -> executor.submit(() -> {
            synchronized (lock) {
                count++;
            }
        }));
        executor.shutdown();
        while (!executor.isTerminated()) {}
        System.out.println("带锁情况下的count: " + count);
    }
}

运行结果每次都是准确的10000,因为锁保证了同一时间只有一个线程能操作count,完全避免了竞态条件。

简单来说:synchronized的核心作用是给共享资源的操作加“互斥锁”,保证线程安全;无锁情况下,多个线程同时操作共享变量会互相干扰,导致结果不可预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:12:56