编程后端开发工程师

synchronized在Java中是如何工作的?何时应用它以及在同步访问资源时存在哪些潜在问题?

用 Hintsage AI 助手通过面试

回答。

问题历史:

自Java问世以来,开发者面临着竞争访问共享资源的问题。为了解决这个问题,引入了高级同步原语,最重要的是关键修饰符synchronized

问题:

在多线程应用中,如果没有同步,共享资源可能会被破坏:会出现数据竞争,导致对象状态变得不可预测。

解决方案:

synchronized允许对方法或代码块进行监视,从而确保在任何时刻只有一个线程可以访问临界区。线程同步可以在方法级别或块级别实现。

对象锁定示例:

public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }

也可以同步代码块:

public void safeIncrement() { synchronized(this) { count++; } }

关键特点:

  • 方法级别的同步等同于在this级别的同步
  • 对于静态方法,以类对象同步
  • 必须尽量减少临界区的范围

具有陷阱的问题。

synchronized方法与synchronized块有什么区别?

synchronized方法会锁定当前对象(this)或类(如果是静态方法)。块允许同步代码的特定部分,并选择任何对象来进行锁定。

两个不同线程能否同时进入同一对象的两个不同synchronized方法?

不可以,如果方法是在同一个监视器(this)上同步的。如果使用不同的监视器,则可以。

synchronized修饰符是否会影响线程之间变量的可见性?

是的,进入synchronized块会清除线程缓存并更新变量值(发生在之前的关系)。

常见错误和反模式

  • 在过于“广泛”的对象上进行同步(例如,字符串或类对象)
  • 在同步块内部执行长时间运行的代码
  • 通过synchronized块和非同步部分混合访问同一资源

现实生活中的例子

负面案例

开发者在实例对象上同步类的静态方法,这在通过不同实例使用时不能保证正确性。

优点:

  • 实现简单

缺点:

  • 线程之间意外的数据竞争
  • 难以捕捉的bug

正面案例

所有使用共享资源的方法都在同一个对象监视器上同步,临界区的范围最小。

优点:

  • 线程处理一致的数据
  • 最少的阻塞

缺点:

  • 难以维护和分析互斥锁(死锁),尤其是在有大量同步对象时