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

为何不同Counter2实例的线程竟共用同一锁?Java并发锁问题咨询

问题:不同Counter实例的线程为何似乎竞争同一锁?

代码片段

class Counter2 implements Runnable{

    private int value = 0;

    
    private Integer lock = 0;

    

    public void increment(){
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        value++;
    }

    public void decrement(){
        value--;
    }

    public int getValue() {
        return value;
    }

    @Override
    public void run() {
        synchronized(lock) {
            this.increment();
            System.out.println(String.format("%s incremented value, value = %s", Thread.currentThread().getName(), this.value));
            this.decrement();
            System.out.println(String.format("%s decremented value, value = %s", Thread.currentThread().getName(), this.value));
        }
    }
}
public class Part8_synchronized_keyword_1 {
    
    public static void main(String[] args) {
        Counter2 counter = new Counter2();
        new Thread(counter,"Thread1").start();
        new Thread(counter,"Thread2").start();
        new Thread(counter,"Thread3").start();
        new Thread(counter,"Thread4").start();
        Counter2 counterB = new Counter2();
        new Thread(counterB, "ThreadB").start();

    }
}

运行输出

Thread1 incremented value, value = 1
Thread1 decremented value, value = 0
ThreadB incremented value, value = 1
ThreadB decremented value, value = 0
Thread4 incremented value, value = 1
Thread4 decremented value, value = 0
Thread3 incremented value, value = 1
Thread3 decremented value, value = 0
Thread2 incremented value, value = 1
Thread2 decremented value, value = 0

问题

为何ThreadB似乎与Thread1至Thread4在竞争同一锁?按我的理解,counter和counterB的lock对象应该是不同的。

解答

核心原因是Integer的自动装箱缓存机制:

  • 当你写private Integer lock = 0;时,Java会自动把int类型的0装箱成Integer对象。
  • Java为了优化性能,会缓存-128到127之间的Integer对象,也就是说所有指向这个范围数值的Integer引用,都会复用同一个对象实例。
  • 所以counter和counterB的lock变量,其实引用的是同一个Integer对象,自然会导致所有线程都在竞争同一把锁,出现串行执行的情况。

修复方案

可以通过以下几种方式避免这个问题:

  1. 使用new Integer(0)创建锁对象,绕过缓存:
    private Integer lock = new Integer(0);
    
  2. 改用Object作为锁对象(更推荐,因为Object不会有缓存问题):
    private Object lock = new Object();
    
  3. 直接用当前对象实例this作为锁(如果不需要区分锁粒度的话):
    synchronized(this) {
        // ...
    }
    

内容的提问来源于stack exchange,提问作者H-Bar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 20:03:15