编程后端开发者

描述一下Java中synchronized块是如何工作的。它的特点是什么,如何选择同步对象,以及不正确的选择可能导致什么错误?

用 Hintsage AI 助手通过面试

答案。

synchronized是一个关键字,允许安全地访问代码中的关键部分。它可以用于方法(例如public synchronized void foo())或代码块(synchronized(obj) { ... })。当线程进入synchronized块时,它会获得对象的监视器(锁)。当监视器被占用时,其他线程无法进入另一个使用相同对象作为监视器的synchronized块。

特点:

  • 对同一对象的同步保证了互斥性(只有一个线程可以同时执行该块)。
  • 可以根据this、静态对象(例如类)或任意对象进行同步。
  • 如果选择一个被多个线程访问的对象,或者相反,选择一个太局部的对象,可能会破坏线程安全。

选择同步对象的示例

public class Counter { private int count; private final Object lock = new Object(); // 私有对象 public void increment() { synchronized(lock) { count++; } } }

为什么最好使用私有锁? 因为如果根据公共对象(例如this或公共字符串)进行同步,外部代码也可能会获得这个监视器,从而导致死锁或不正确的工作。

陷阱问题。

问题: 如果根据包含固定值的String对象进行同步,会发生什么?

答案: 在Java中,字符串是被池化的(相同字面量对应相同对象)。如果根据synchronized("lock")进行同步,可能会意外与其他同步在相同字面量上的代码发生冲突,这将导致程序的不同部分之间意外的锁定。

示例(不可这样做):

synchronized("LOCK") { ... }

由于对此主题的不了解而导致的实际错误示例。


故事

在一个多线程交易系统中,为同步使用了公共对象,外部代码能够获得锁,导致不同模块的线程之间发生临时死锁,导致交易所停滞。


故事

一位年轻的开发者根据字符串字面量同步对集合的访问。代码的另一部分也根据相同值的字符串进行同步。这些线程排队,导致业务逻辑的显著减慢。


故事

每次同步时选择一个新的对象:synchronized(new Object()) { ... }。结果是同步根本不工作,多个线程同时访问数据。这只有在压力测试中才能被发现。