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

Java多线程同步疑问:synchronized(this)为何无法保证结果为0?

问题解析与解决方案

嗨,我来帮你把这个问题掰明白~

首先,你调试发现的this对象始终不同,就是导致结果不对的核心原因!

为什么this会不一样?

你创建两个线程时,分别用了两个独立的匿名Runnable内部类实例:

  • 第一个线程的Runnable是一个全新的对象,它的this指向自己这个Runnable实例;
  • 第二个线程的Runnable又是另一个全新的对象,它的this指向自己的Runnable实例。

这就意味着两个线程的synchronized (this)其实是在锁完全不同的两把锁,相当于根本没起到同步的作用——两个线程可以同时对value进行读写操作。

为什么结果不是0?

虽然value被声明为volatile,但volatile只能保证变量的可见性(一个线程修改后,其他线程能立刻看到最新值),却不能保证操作的原子性。

++value和--value其实都包含三个步骤:

  1. 读取当前value的值;
  2. 对值进行加1/减1运算;
  3. 将运算结果写回value。

当两个线程同时执行这三个步骤时,就会出现“竞态条件”:比如线程A刚读到value=5,还没来得及写回;线程B也读到了value=5,最后一个加1变成6,一个减1变成4,两者的操作就相互覆盖了,最终计数就会偏差。

怎么修复这个问题?

有两种常见的解决方案:

方案1:使用同一个共享锁对象

既然要同步对静态变量value的操作,我们可以定义一个全局共享的锁对象,让两个线程都锁这个对象:

public class Lesson6 { 
    private static volatile Long value = 0L;
    // 定义一个静态的共享锁对象
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException { 
        Thread inc = new Thread(new Runnable() { 
            @Override 
            public void run() { 
                for (int i = 0; i < 100_000; i++) { 
                    synchronized (lock){ // 锁同一个对象
                        ++value; 
                    } 
                } 
            } 
        }); 
        inc.start(); 

        Thread dec = new Thread(new Runnable() { 
            @Override 
            public void run() { 
                for (int i = 0; i < 100_000; i++) { 
                    synchronized (lock){ // 锁同一个对象
                        --value; 
                    } 
                } 
            } 
        }); 
        dec.start(); 

        inc.join(); 
        dec.join(); 
        System.out.println(value); 
    } 
}

方案2:使用原子类(更推荐)

Java的java.util.concurrent.atomic包提供了一系列原子操作类,比如AtomicLong,它的增减操作本身就是原子性的,不需要手动加锁:

import java.util.concurrent.atomic.AtomicLong;

public class Lesson6 { 
    // 用AtomicLong代替volatile Long
    private static final AtomicLong value = new AtomicLong(0L);

    public static void main(String[] args) throws InterruptedException { 
        Thread inc = new Thread(new Runnable() { 
            @Override 
            public void run() { 
                for (int i = 0; i < 100_000; i++) { 
                    value.incrementAndGet(); // 原子加1
                } 
            } 
        }); 
        inc.start(); 

        Thread dec = new Thread(new Runnable() { 
            @Override 
            public void run() { 
                for (int i = 0; i < 100_000; i++) { 
                    value.decrementAndGet(); // 原子减1
                } 
            } 
        }); 
        dec.start(); 

        inc.join(); 
        dec.join(); 
        System.out.println(value.get()); 
    } 
}

这样修改后,你就能得到预期的输出0啦~

内容的提问来源于stack exchange,提问作者Константин Крылов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:00:02