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区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java使用堆栈跟踪工具jstack的实现

    Java使用堆栈跟踪工具jstack的实现

    jstack是Java Development Kit(JDK)的一部分,用于打印Java虚拟机(JVM)中所有线程的堆栈跟踪信息,本文就来介绍一下Java使用堆栈跟踪工具jstack的实现,感兴趣的可以了解一下
    2025-10-10
  • java图形界面编程实战代码

    java图形界面编程实战代码

    这篇文章主要介绍了java图形界面编程实战代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • 关于重写equals()方法和hashCode()方法及其简单的应用

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

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

    SWT(JFace)体验之StackLayout布局

    SWT(JFace)体验之StackLayout布局实现代码。
    2009-06-06
  • Java几个实例带你进阶升华下篇

    Java几个实例带你进阶升华下篇

    与其明天开始,不如现在行动,本文为你带来几个Java书写的实际案例,对巩固编程的基础能力很有帮助,快来一起往下看看吧
    2022-03-03
  • 浅谈Redis在微服务架构中的几种应用场景

    浅谈Redis在微服务架构中的几种应用场景

    本文介绍在SpringCloud中使用Redis作为Pub/Sub异步通信、缓存或主数据库和配置服务器的三种场景应用。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • java获取优酷视频地址示例

    java获取优酷视频地址示例

    使用JAVA解析优酷视频页,得到视频真实地址, 还可以下载,需要的朋友可以参考下
    2014-03-03
  • sprng和struts有什么区别?

    sprng和struts有什么区别?

    Spring和Struts都是近年来比较流行的框架,Struts主要用于表示层,Spring用于业务层,以及Hiberate主要用于持久层,
    2015-06-06
  • Java实时获取基金收益项目源码分享

    Java实时获取基金收益项目源码分享

    这篇文章主要介绍了Java实时获取基金收益项目源码分享,主要包括JAVA爬取天天基金网数据使用实例、应用技巧、基本知识点总结和需要注意事项,需要的朋友可以参考下
    2021-03-03
  • 详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    这篇文章主要介绍了详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05

最新评论