Java高频面试之并发编程-18

hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶

面试官:详细说说synchronized

synchronized 是 Java 中实现线程同步的核心关键字,用于解决多线程环境下的资源竞争问题,确保线程安全。


1. 基本作用

synchronized 通过 互斥锁(Mutex Lock) 机制,保证同一时刻只有一个线程能访问被保护的代码块或方法,避免多个线程同时操作共享资源导致的数据不一致问题。


2. 使用方式

synchronized 可以修饰以下三种对象:

(1) 实例方法
  • 锁对象 :当前实例对象(this)。

  • 作用 :同一实例的多个线程会互斥访问该方法。

    java 复制代码
    public synchronized void method() {
        // 同步代码
    }
(2) 静态方法
  • 锁对象 :类的 Class 对象(MyClass.class)。

  • 作用 :所有实例的线程都会互斥访问该静态方法。

    java 复制代码
    public static synchronized void staticMethod() {
        // 同步代码
    }
(3) 代码块
  • 锁对象:显式指定任意对象(通常是共享资源)。

  • 作用 :更细粒度地控制同步范围。

    java 复制代码
    public void blockMethod() {
        synchronized (lockObject) {  // lockObject 可以是任意对象
            // 同步代码
        }
    }

3. 核心特性

(1) 可重入性(Reentrant)
  • 同一个线程可以重复获取同一个锁。
  • 示例:线程获取锁后,在同步代码中调用另一个同步方法(使用同一锁)不会阻塞。
(2) 锁的释放
  • 线程执行完同步代码块或方法后,自动释放锁
  • 若线程发生异常退出同步代码块,锁也会自动释放。
(3) 锁的竞争
  • 未获取锁的线程会进入 阻塞状态(BLOCKED),直到锁被释放。

4. 底层原理

synchronized 的底层实现依赖于 JVM 的 Monitor(监视器锁) 机制,具体通过以下步骤实现:

(1) Monitor 对象
  • 每个 Java 对象都与一个 Monitor 关联,Monitor 包含以下关键字段:
    • _owner:记录当前持有锁的线程。
    • _EntryList:等待获取锁的阻塞线程队列。
    • _WaitSet:调用 wait() 后进入等待状态的线程队列。
(2) 字节码层面
  • 同步代码块通过 monitorentermonitorexit 指令实现:

    java 复制代码
    public void method() {
        synchronized (obj) {
            // 代码
        }
    }

    对应的字节码:

    复制代码
    monitorenter   // 尝试获取锁
    ...             // 同步代码
    monitorexit    // 释放锁
(3) 锁升级优化(JDK 1.6+)

为了提高性能,JVM 对 synchronized 进行了优化,引入了 锁升级机制

  1. 无锁(No Lock):初始状态。
  2. 偏向锁(Biased Lock):锁偏向第一个获取它的线程,避免后续 CAS 操作。
  3. 轻量级锁(Lightweight Lock):通过 CAS 自旋尝试获取锁,避免线程阻塞。
  4. 重量级锁(Heavyweight Lock):竞争激烈时,升级为操作系统级别的互斥锁(Mutex)。

5. 典型应用场景

(1) 线程安全的单例模式
java 复制代码
public class Singleton {
    private static volatile Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
(2) 保护共享资源
java 复制代码
public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }
}

6. 与 ReentrantLock 的对比

特性 synchronized ReentrantLock
锁类型 JVM 内置锁 JDK 实现的显式锁
灵活性 不支持中断、超时、公平锁 支持中断、超时、公平锁
代码控制 自动释放锁 需手动 lock()unlock()
性能 JDK 1.6 后优化,性能接近显式锁 高竞争场景下性能更好
适用场景 简单同步需求 复杂同步逻辑(如条件变量 Condition

7. 注意事项

  1. 锁对象的选择

    • 避免使用字符串常量或基本类型(如 Integer)作为锁对象。
    • 推荐使用私有、不可变的对象:private final Object lock = new Object();
  2. 减少同步范围

    • 尽量缩小同步代码块,避免在同步块内执行耗时操作(如 I/O)。
  3. 死锁风险

    • 避免嵌套锁或多锁顺序不一致导致的死锁。
    • 示例:线程 A 先锁 X 再锁 Y,线程 B 先锁 Y 再锁 X → 可能死锁。
  4. 锁粗化与锁消除

    • 锁粗化:JVM 将多个连续锁合并为一个锁,减少开销。
    • 锁消除:JVM 检测到不可能存在共享竞争时,自动去除锁(如局部变量)。

8. 常见问题

Q1:synchronizedvolatile 的区别?
  • synchronized:保证原子性、可见性和有序性。
  • volatile:仅保证可见性和有序性,不保证原子性(如 i++ 仍需同步)。
Q2:静态方法和实例方法的锁是否冲突?
  • 不冲突!静态方法锁的是类对象(Class),实例方法锁的是当前实例(this)。
Q3:如何排查死锁?
  • 使用 jstack 或可视化工具(如 JConsole)查看线程状态和锁持有情况。

总结

synchronized 是 Java 线程同步的基石,通过 Monitor 机制实现互斥访问。虽然在高并发场景下可能成为性能瓶颈,但其简洁性和 JVM 的优化(如锁升级)使其在大多数场景下足够高效。对于更复杂的同步需求,可结合 ReentrantLock 或并发工具类(如 SemaphoreCountDownLatch)使用。

相关推荐
果粒多10 分钟前
JVM 参数调优核心原则与常用参数
java·开发语言·jvm
C++ 老炮儿的技术栈11 分钟前
visual studio 2022更改主题为深色
c语言·开发语言·c++·ide·windows·git·visual studio
java龙王*14 分钟前
python爬虫简便框架,附带百度操作完整案例
开发语言·爬虫·python
安全系统学习34 分钟前
网络安全之内核初级对抗技术分析
开发语言·python·算法·安全·web安全
南棱笑笑生37 分钟前
20250614在Ubuntu20.04.6下分步骤编译Rockchip的RK3576原厂SDK
java·开发语言·git
源码宝1 小时前
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
java·大数据·源码·智慧工地·智能监测·智能施工
Morpheon1 小时前
R语言非结构化文本挖掘入门指南
开发语言·r语言
码不停蹄的玄黓2 小时前
JUC核心解析系列(五)——执行框架(Executor Framework)深度解析
java·jvm·spring boot·spring cloud
白总Server2 小时前
GaussDB 分布式数据库调优(架构到全链路优化)
java·网络·c++·架构·go·scala·数据库架构
工呈士2 小时前
构建优化策略:Tree Shaking、代码分割与懒加载
前端·面试