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

Java并发编程:为何需单独创建锁对象而非使用内置锁?

为什么不直接用目标对象做锁,而是单独创建锁对象?

先看你第一个Integer的例子,这么写完全起不到同步效果。因为Integer是不可变类,foo++本质是foo = Integer.valueOf(foo.intValue() + 1),会生成一个新的Integer对象。这意味着每个线程进入synchronized(foo)时,锁的是不同的对象,线程之间根本不会互斥,同步逻辑直接失效。

回到核心问题:哪怕是引用不会变的对象(比如static Object),直接用它的内置锁也有不少弊端,单独创建锁对象是更安全、更规范的做法,原因如下:

1. 避免锁对象引用被意外修改

如果用目标资源对象当锁,一旦代码中不小心修改了该对象的引用(比如替换了static Socket实例),后续线程会锁在新对象上,旧线程还持有旧对象的锁,同步逻辑直接崩溃。而单独创建的锁对象一般会用private final修饰,引用永远不会变,能保证锁的一致性。

2. 防止外部代码干扰,降低死锁风险

如果目标对象是public或者能被外部访问到的(比如static Socket),外部代码也可能对这个对象加锁。比如其他类里写synchronized(YourClass.yourSocket),就会和你内部的同步代码产生锁竞争,甚至导致死锁。而单独创建的私有锁对象,只有当前类能访问,完全避免这种外部干扰。

3. 提升代码可读性与封装性

单独的锁对象(比如fooLock、socketLock)语义明确,一看就知道它的作用是控制同步逻辑。而用目标资源当锁,其他开发者可能会疑惑:这个锁是用来保护资源本身,还是资源的某个操作?同时,锁作为内部实现细节,应该被封装起来,不暴露给外部,符合面向对象的封装原则。

关于synchronized(this)的问题

synchronized(this)和直接用目标对象的问题类似:外部代码可以拿到当前类的实例,然后对实例加锁,导致和内部的同步逻辑竞争。比如外部代码写synchronized(yourInstance),就会阻塞内部用synchronized(this)的线程,反之亦然,容易引发意外的性能问题或死锁。

针对static Socket的场景

如果你直接用static Socket当锁,除了上面提到的引用被修改、外部干扰的问题,还要注意:Socket类本身的某些方法可能已经使用了自身的内置锁(比如同步的IO方法),你再在外层加锁,会导致锁嵌套,增加死锁的可能性。更稳妥的做法是创建一个private static final Object socketLock = new Object();,用它来控制对Socket的同步访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 03:48:42