Java中Synchronized与Lock锁机制的区别详解

 更新时间:2026年05月07日 09:43:46   作者:身如柳絮随风扬  
在 Java 并发编程中,synchronized 和 Lock 是最常用的两种锁机制,它们都能保证线程安全,但实现原理、功能特性和性能表现上有显著差异,下面小编就和大家详细介绍一下吧

在 Java 并发编程中,synchronizedLock 是最常用的两种锁机制。它们都能保证线程安全,但实现原理、功能特性和性能表现上有显著差异。本文将带你彻底搞懂两者的区别,并通过流程图和对比表格助你做出正确的技术选型。

一、引言

线程安全是并发编程的核心问题。Java 提供了多种锁机制,其中 synchronized 是 JVM 内置的关键字,而 Lockjava.util.concurrent.locks 包下的接口(典型实现如 ReentrantLock)。许多开发者只知道“Lock 更灵活”,却不清楚具体灵活在哪里,以及什么场景该用哪个。

本文将围绕以下维度展开对比:

  • 语法与使用方式
  • 锁的获取与释放机制
  • 可中断性、超时尝试
  • 公平性选择
  • 底层实现(偏向锁、轻量级锁、重量级锁 vs AQS)
  • 性能表现(JDK 1.6 前后的变化)

最后给出实战建议与流程图,帮助你直观理解两者在锁竞争时的行为差异。

二、一句话总结核心区别(先入为主)

特性synchronizedLock(以 ReentrantLock 为例)
实现层级JVM 关键字,内置语言特性Java 类库,基于 AQS 的 API
锁的获取方式隐式自动获取/释放显式调用 lock() / unlock()
可中断性不可中断(除非抛出异常)支持 lockInterruptibly()
超时获取锁不支持支持 tryLock(timeout, unit)
公平锁非公平锁(仅)可公平(构造参数)也可非公平
条件变量每个对象只有一个等待队列(wait/notify)一个 Lock 可绑定多个 Condition
是否可重入是(ReentrantLock 可实现)
锁释放方式自动(方法结束或异常)必须手动在 finally 中释放
锁状态监控无直接 API提供 tryLock()、isHeldByCurrentThread() 等
性能(低竞争)较好(JVM 优化,偏向锁)略差(对象创建开销)
性能(高竞争)早期较差,JDK 1.6 后改善稳定可控,吞吐量有时更高

三、深入对比:原理与代码示例

1. 语法与使用方式

synchronized:修饰实例方法、静态方法或代码块。

// 实例方法锁(当前实例)
public synchronized void method1() { /* 临界区 */ }

// 静态方法锁(Class 对象)
public static synchronized void method2() { /* 临界区 */ }

// 代码块锁(自定义对象)
public void method3() {
    synchronized (this) {
        // 临界区
    }
}

Lock:需要显式创建锁对象,并在 finally 块中释放。

Lock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock();   // 必须手动释放,否则死锁
}

常见错误:忘记在 finallyunlock(),导致锁无法释放。这也是 synchronized 的“自动释放”优势所在。

2. 锁的可中断性

  • synchronized 线程在等待获取锁时不可被中断Thread.interrupt() 无效,会一直阻塞)。
  • Lock 提供了 lockInterruptibly(),允许等待锁的线程响应中断。
lock.lockInterruptibly();  // 等待时可被中断
try {
    // ...
} catch (InterruptedException e) {
    // 处理中断
} finally {
    lock.unlock();
}

3. 超时获取锁(避免死锁)

synchronized 无法尝试获取锁一段时间后放弃。LocktryLock(long time, TimeUnit unit) 能实现非阻塞尝试:

if (lock.tryLock(1, TimeUnit.SECONDS)) {
    try {
        // 获取成功
    } finally {
        lock.unlock();
    }
} else {
    // 未获取到锁,执行其他逻辑
}

4. 公平性

  • synchronized 只支持非公平锁(等待队列中随机或按操作系统调度,可能线程饿死)。
  • ReentrantLock 可选公平锁:new ReentrantLock(true)。公平锁保证等待时间最长的线程先获得锁,但会降低吞吐量。
Lock fairLock = new ReentrantLock(true);   // 公平锁
Lock unfairLock = new ReentrantLock(false); // 非公平锁(默认)

5. 条件变量(Condition)

synchronized 通过 wait()notify() / notifyAll() 实现等待/通知,每个对象只有一个等待集。
Lock 可以创建多个 Condition 对象,实现更精细的线程唤醒。

ReentrantLock lock = new ReentrantLock();
Condition notFull  = lock.newCondition();
Condition notEmpty = lock.newCondition();

// 生产者
lock.lock();
try {
    while (队列满) notFull.await();
    // 生产
    notEmpty.signal();
} finally { lock.unlock(); }

四、底层实现原理简述

synchronized 的升级过程(JDK 1.6 优化)

在 JDK 1.6 之前,synchronized重量级锁(依赖操作系统的 mutex,用户态到内核态切换代价大)。1.6 之后引入了偏向锁 → 轻量级锁 → 重量级锁的升级过程:

  • 偏向锁:锁总是被同一个线程获取,无需 CAS。
  • 轻量级锁:多线程交替执行,使用 CAS 尝试获取锁,不阻塞。
  • 重量级锁:竞争激烈,阻塞线程。

Lock(ReentrantLock)的 AQS 原理

ReentrantLock 基于 AQS(AbstractQueuedSynchronizer),内部维护一个 FIFO 等待队列和一个 volatile int state 表示锁状态。通过 CAS 设置状态,失败则加入等待队列并阻塞(LockSupport.park())。

两者底层差异决定:

  • synchronized 的锁释放由 JVM 保证(异常或方法结束),不会遗漏。
  • Lock 的灵活性更高,但手动释放容易出错。

五、流程图对比:锁获取流程

synchronized 获取锁流程

Lock(ReentrantLock)获取锁流程

六、性能测试与选型建议

性能历史

  • JDK 1.5synchronized 性能很差,ReentrantLock 明显更优。
  • JDK 1.6+synchronized 经过优化(偏向锁、轻量级锁、适应性自旋),低竞争场景下性能与 Lock 相当甚至略好。
  • 高竞争场景:两者性能差别不大,但 Lock 提供了更多控制(如公平锁、可中断)。

选择指南

场景推荐锁原因
简单同步代码块,无需高级特性synchronized简洁、安全、自动释放
需要尝试获取锁并超时退出LocktryLock 超时机制
需要可中断的锁获取LocklockInterruptibly
需要多个条件变量(生产者-消费者)LockCondition 更灵活
需要公平锁Locksynchronized 不支持
性能要求极高且竞争极低synchronized偏向锁开销小
锁粗粒度,手动控制释放时机Lock可在不同方法间释放

七、实战代码对比:模拟售票系统

以下示例展示两种方式实现线程安全的余票扣除。

使用 synchronized

class TicketSync {
    private int tickets = 100;
    public synchronized boolean sell() {
        if (tickets <= 0) return false;
        tickets--;
        return true;
    }
}

使用 ReentrantLock

class TicketLock {
    private int tickets = 100;
    private final Lock lock = new ReentrantLock();
    public boolean sell() {
        lock.lock();
        try {
            if (tickets <= 0) return false;
            tickets--;
            return true;
        } finally {
            lock.unlock();
        }
    }
}

八、常见误区

1.“Lock 一定比 synchronized 快”

错。JDK 1.6 后,低竞争下 synchronized 有偏向锁优化,性能不差;高竞争下两者几乎持平。

2.“synchronized 不可重入”

错。synchronized可重入锁,同一个线程可以多次进入。

3.“Lock 必须要 try-finally,否则死锁”

对。若临界区抛出异常未释放锁,其他线程将永久等待。synchronized 由 JVM 保证释放。

4.“公平锁一定比非公平锁好”

错。公平锁减少了线程饿死,但上下文切换更频繁,吞吐量通常更低。

九、总结与思维导图

最终建议

  • 优先使用 synchronized,除非遇到必须使用 Lock 的场景(如超时、中断、多条件)。
  • 永远不要自己实现锁,使用 JUC 包下的 Lock 实现。
  • 手动 Lock 时,unlock() 必须放在 finally 块中。

到此这篇关于Java中Synchronized与Lock锁机制的区别详解的文章就介绍到这了,更多相关Java Synchronized与Lock区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用vscode搭建javaweb项目的详细步骤

    使用vscode搭建javaweb项目的详细步骤

    我个人是很喜欢VsCode的,开源免费、功能全面,所以为了方便,我把我几乎所有的运行都集成到了VsCode上来,JavaWeb也不例外,下面这篇文章主要给大家介绍了关于使用vscode搭建javaweb项目的相关资料,需要的朋友可以参考下
    2022-11-11
  • java交换排序之奇偶排序实现方法

    java交换排序之奇偶排序实现方法

    这篇文章主要介绍了java交换排序之奇偶排序实现方法,实例分析了奇偶排序的原理与具体实现技巧,非常具有实用价值,需要的朋友可以参考下
    2015-02-02
  • MyBatis中多对一和一对多数据的处理方法

    MyBatis中多对一和一对多数据的处理方法

    这篇文章主要介绍了MyBatis中多对一和一对多数据的处理,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-01-01
  • Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

    Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

    Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前修改或扩展Bean的配置元数据,本文给大家介绍Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析,感兴趣的朋友一起看看吧
    2025-03-03
  • java 静态代理 动态代理深入学习

    java 静态代理 动态代理深入学习

    代理模式是常用的java设计模式,特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,需要的朋友可以参考下
    2012-11-11
  • JavaSE文件操作工具类FileUtil详解

    JavaSE文件操作工具类FileUtil详解

    这篇文章主要为大家详细介绍了JavaSE系列之文件操作工具类FileUtil,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • DoytoQuery中的查询映射方案详解

    DoytoQuery中的查询映射方案详解

    这篇文章主要为大家介绍了DoytoQuery中的查询映射方案详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • SpringBoot请求体缺失异常原因分析与处理方案

    SpringBoot请求体缺失异常原因分析与处理方案

    这篇文章主要介绍了SpringBoot请求体缺失异常原因分析与处理方案,该异常是因为UserController的edit方法缺失了预期的UserUpdatePwdDTO请求体,产生原因主要由前端未发送请求体、Content-Type设置错误或后端缺少@RequestBody注解导致,需要的朋友可以参考下
    2025-10-10
  • SpringBoot实现数据加密脱敏的示例代码

    SpringBoot实现数据加密脱敏的示例代码

    这篇文章主要为大家学习介绍了SpringBoot如何利用注解+反射+AOP实现数据加密脱敏的功能,文中的示例代码讲解详细,需要的可以参考一下
    2023-08-08
  • 关于重写equals()方法和hashCode()方法及其简单的应用

    关于重写equals()方法和hashCode()方法及其简单的应用

    这篇文章主要介绍了关于重写equals()方法和hashCode()方法及其简单的应用,网上的知识有些可能是错误的,关于 equals() 方法的理解,大家讨论不一样,需要的朋友可以参考下
    2023-04-04

最新评论