当前位置: 首页 > news >正文

synchronized 和 ReentrantLock 的区别

面试资料大全|各种技术资料-2000G

在 Java 并发编程中,synchronizedReentrantLock 是两种最常用的锁机制,它们在实现线程同步方面各有特点。

一、核心区别全景图

特性synchronizedReentrantLock
实现级别JVM 原生关键字JDK API 类 (java.util.concurrent.locks)
锁获取方式自动获取与释放手动 lock()/unlock()
锁类型非公平锁(默认)可选择公平锁/非公平锁
尝试获取锁不支持支持 tryLock()/tryLock(timeout)
条件变量单一等待队列支持多个 Condition 队列
锁中断不支持中断等待支持 lockInterruptibly()
性能Java 6+ 优化后相当高竞争场景下更优
锁绑定与方法/代码块绑定可跨方法使用
监控工具JVM 内置监控提供 getHoldCount() 等监控方法
代码复杂度简单易用需要显式释放锁

二、实现原理剖析

1. synchronized 底层机制

public synchronized void method() {// 临界区代码
}// 等价于:
public void method() {this.monitor.enter();  // JVM 指令: monitorentertry {// 临界区代码} finally {this.monitor.exit(); // JVM 指令: monitorexit}
}

锁升级过程

首次访问
竞争发生
竞争加剧
竞争减少
无锁状态
偏向锁
轻量级锁
重量级锁

2. ReentrantLock 实现原理

final ReentrantLock lock = new ReentrantLock();public void method() {lock.lock();  // 基于AQS实现try {// 临界区代码} finally {lock.unlock(); // 必须手动释放}
}

AQS (AbstractQueuedSynchronizer) 核心

AQS
-volatile int state
-Node head
-Node tail
+acquire()
+release()
Node
-Thread thread
-Node prev
-Node next
-int waitStatus

三、关键特性对比详解

1. 公平性控制

synchronized

  • 仅支持非公平锁(线程竞争时不排队)

ReentrantLock

// 创建公平锁
ReentrantLock fairLock = new ReentrantLock(true); // 创建非公平锁(默认)
ReentrantLock unfairLock = new ReentrantLock(); 
  • 公平锁:按等待顺序分配锁
  • 非公平锁:允许插队获取锁

2. 锁获取灵活性

synchronized 局限

  • 阻塞式获取,无法中断等待
  • 无法设置超时

ReentrantLock 增强

// 尝试获取锁(立即返回)
if (lock.tryLock()) {try { /* 操作 */ } finally { lock.unlock(); }
}// 带超时尝试
if (lock.tryLock(3, TimeUnit.SECONDS)) {// ...
}// 可中断获取
try {lock.lockInterruptibly();// ...
} catch (InterruptedException e) {// 处理中断
}

3. 条件变量(Condition)

synchronized

  • 仅支持 wait()/notify() 单一等待队列

ReentrantLock

ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();  // 条件1:非满
Condition notEmpty = lock.newCondition(); // 条件2:非空// 生产者
public void put(Object item) {lock.lock();try {while (isFull()) notFull.await(); // 等待非满条件// ... 添加元素notEmpty.signal();   // 唤醒消费者} finally { lock.unlock(); }
}// 消费者
public Object take() {lock.lock();try {while (isEmpty()) notEmpty.await(); // 等待非空条件// ... 获取元素notFull.signal();     // 唤醒生产者} finally { lock.unlock(); }
}

四、性能对比与优化

1. 历史性能演进

Java 版本synchronized 性能ReentrantLock 性能
Java 5差(重量级锁)显著优于 synchronized
Java 6引入偏向锁/轻量级锁,大幅提升仍优但差距缩小
Java 15+接近 ReentrantLock高竞争场景仍保持优势

2. 基准测试数据(8线程竞争)

操作synchronized (ns/op)ReentrantLock (ns/op)
无竞争获取锁1520
中等竞争12095
高竞争(16线程)450280

数据来源:Oracle 官方性能测试 (JMH)

五、使用场景指南

优先使用 synchronized:

// 简单同步场景
public class Counter {private int count;public synchronized void increment() {count++;}
}
  • ✅ 简单临界区保护
  • ✅ 单方法内的同步
  • ✅ 不需要高级特性

优先使用 ReentrantLock:

// 复杂同步控制
public class BankTransfer {private final ReentrantLock lock = new ReentrantLock();public boolean transfer(Account from, Account to, double amount) {// 尝试获取两个账户的锁if (!from.lock.tryLock()) return false;try {if (!to.lock.tryLock()) return false;try {// 转账逻辑return true;} finally {to.lock.unlock();}} finally {from.lock.unlock();}}
}
  • ✅ 需要尝试获取锁(tryLock)
  • ✅ 需要公平锁机制
  • ✅ 需要多个条件变量
  • ✅ 需要可中断的锁获取
  • ✅ 需要跨方法加锁/解锁

六、最佳实践与陷阱规避

1. ReentrantLock 正确用法

ReentrantLock lock = new ReentrantLock();// 正确:必须用 try-finally 确保释放
lock.lock();
try {// 临界区操作
} finally {lock.unlock(); // 确保在任何情况下都释放锁
}// 错误:忘记解锁(导致死锁)
lock.lock();
// 临界区操作
return; // 如果此处返回,锁永远不会释放!

2. 避免嵌套陷阱

// synchronized 嵌套(正确)
public synchronized void methodA() {methodB(); // 可重入
}public synchronized void methodB() {// ...
}// ReentrantLock 嵌套
public void methodA() {lock.lock();try {methodB(); // 注意重入次数} finally {lock.unlock();}
}public void methodB() {lock.lock();   // 再次获取锁(重入)try { /* ... */ } finally { lock.unlock(); // 必须匹配解锁// 如果此处缺少unlock,锁不会完全释放!}
}

3. 锁性能优化建议

  1. 减小锁粒度

    // 不推荐:锁整个对象
    public synchronized void process() { /* 耗时操作 */ }// 推荐:只锁必要部分
    public void process() {// 非同步代码...synchronized(this) {// 临界区}
    }
    
  2. 读写分离

    // 使用 ReentrantReadWriteLock
    ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    rwLock.readLock().lock();   // 多个读线程可同时进入
    rwLock.writeLock().lock();  // 写线程独占
    
  3. 锁分段

    // ConcurrentHashMap 的分段锁思想
    private final Lock[] locks = new ReentrantLock[16];void update(int key) {int index = key % locks.length;locks[index].lock();try { /* 操作对应分段 */ } finally { locks[index].unlock(); }
    }
    

七、现代 Java 的锁发展

1. 虚拟线程(Java 21+)

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {executor.submit(() -> {// 在虚拟线程中使用synchronized更安全synchronized(lockObj) {// 不会阻塞操作系统线程}});
}

2. 结构化并发(Java 21+)

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {Future<String> user = scope.fork(() -> fetchUser());Future<Integer> order = scope.fork(() -> fetchOrder());scope.join();          // 等待所有任务scope.throwIfFailed(); // 处理异常// 自动管理线程生命周期
}

选型建议

1. 选择 synchronized 当

  • 需要简单的线程同步
  • 锁获取释放在同一方法内
  • 不需要高级锁特性
  • 追求代码简洁性

2. 选择 ReentrantLock 当

  • 需要尝试获取锁(tryLock)
  • 需要公平锁机制
  • 需要多个条件变量
  • 需要可中断的锁获取
  • 需要锁的细粒度控制
  • 需要跨方法加锁/解锁

3. 通用准则

  • 优先使用 synchronized:Java 6+ 后性能已大幅优化,代码更简洁
  • 有明确需求时用 ReentrantLock:当 synchronized 无法满足高级特性时
  • 避免过度设计:不要为不存在的性能问题提前优化

面试资料大全|各种技术资料-2000G

http://www.lqws.cn/news/464617.html

相关文章:

  • 探索 Oracle Database 23ai 中的 SQL 功能
  • 团结引擎 1.5.0 更新 | OpenHarmony 平台开发体验全面升级,突破游戏类应用帧率限制
  • CertiK联创顾荣辉将于港大活动发表演讲,分享Web3安全与发展新视角
  • (LeetCode 面试经典 150 题) 80. 删除有序数组中的重复项 II (双指针、栈)
  • AI与SEO关键词协同进化
  • SQL关键字三分钟入门:INSERT INTO —— 插入数据详解
  • Armbian 开机启动点灯脚本
  • 【C++特殊工具与技术】局部类
  • 从事登高架设作业需要注意哪些安全事项?
  • 57-Oracle SQL Profile(23ai)实操
  • 内容搜索软件AnyTXT.Searcher忘记文件名也能搜,全文检索 1 秒定位文件
  • Java求职者面试指南:微服务技术与源码原理深度解析
  • Rabbitmq集成springboot,手动确认消息basicAck、basicNack、basicReject的使用
  • 在微信小程序wxml文件调用函数实现时间转换---使用wxs模块实现
  • WevServer实现:异步日志写与HTTP连接
  • Zephyr 调试实用指南:日志系统、Shell CLI 与 GDB 全面解析
  • CLion开发Qt桌面程序_git的简单使用_小团体
  • 闲庭信步使用SV搭建图像测试平台:第五课——使用task
  • pyqt logger类与界面分开
  • 从语义到推荐:大语言模型(LLM)如何驱动智能选车系统?
  • Mac电脑-Markdown编辑器-Typora
  • c++26新功能—hive容器
  • 税务 VR 虚拟体验,带来全新办税感受
  • 【软考高级系统架构论文】论NoSQL数据库技术及其应用
  • HarmonyOS 5的分布式通信矩阵是如何工作的?
  • 鸿蒙NEXT-鸿蒙三层架构搭建,嵌入HMRouter,实现便捷跳转,新手攻略。(1/3)
  • 在AI普及的大环境下神经网络在新能源汽车热管理系统中的应用简介
  • 无线Debugger攻防全解:原理剖析与突破之道
  • Qt中的布局
  • 深入浅出:Go语言中的Cookie、Session和Token认证机制