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

一文解析公平锁、非公平锁、悲观锁、乐观锁、可重入锁和锁的升级(含详细代码实例)

在并发开发和数据库事务处理中,锁机制是绕不开的难题。了解锁的种类、原理和应用场景,对于写出高效安全的并发程序非常重要。本文将系统总结公平锁、非公平锁、悲观锁、乐观锁、可重入锁、锁的升级等核心知识,并配套详细的实用代码,带你搞懂常见锁机制!


一、公平锁与非公平锁

1. 公平锁(Fair Lock)

定义:获取锁的顺序按照线程到来的顺序进行分配,先请求锁的线程先获得锁,避免线程“饥饿”。

应用场景:对执行先后顺序非常敏感的场合,如任务调度、并发队列等。

代码示例(Java)

import java.util.concurrent.locks.ReentrantLock;public class FairLockDemo {// true 表示公平锁private static final ReentrantLock fairLock = new ReentrantLock(true);public void testLock() {fairLock.lock();try {System.out.println(Thread.currentThread().getName() + " 获得了公平锁");Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();} finally {fairLock.unlock();}}public static void main(String[] args) {FairLockDemo demo = new FairLockDemo();for (int i = 0; i < 5; i++) {new Thread(demo::testLock, "Thread-" + i).start();}}
}

2. 非公平锁(Nonfair Lock)

定义:线程加锁的顺序不严格按照申请顺序,允许“插队”,提高吞吐量。

应用场景:大多数并发场景,追求性能。

代码示例(Java)

import java.util.concurrent.locks.ReentrantLock;public class NonFairLockDemo {// 默认 false,非公平锁private static final ReentrantLock lock = new ReentrantLock();public void testLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() + " 获得了非公平锁");Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {NonFairLockDemo demo = new NonFairLockDemo();for (int i = 0; i < 5; i++) {new Thread(demo::testLock, "Thread-" + i).start();}}
}

拓展: synchronized 在 JVM 层面也是一种非公平锁。


二、悲观锁与乐观锁

1. 悲观锁(Pessimistic Lock)

定义:假设系统“很可能”发生并发冲突,每次数据操作都先加锁,其他线程只能等待。

应用场景:并发写多于读,且数据一致性要求高,例如订单、资金等核心业务。

数据库实现:

-- 事务中加锁(MySQL InnoDB)
START TRANSACTION;
SELECT * FROM account WHERE id=1 FOR UPDATE;
-- do something...
UPDATE account SET balance=balance-100 WHERE id=1;
COMMIT;

Java实现(synchronized / Lock):

public class PessimisticLockDemo {public synchronized void doSomething() {System.out.println(Thread.currentThread().getName() + " 获得了悲观锁");// 执行“临界区”操作}
}

2. 乐观锁(Optimistic Lock)

定义:假定冲突较少,不上锁,通过“版本号”或CAS检查数据是否被别人修改。若被修改则重试。

应用场景:用户数大、并发冲突不多的场景,如商品秒杀、用户信息查询等。

数据库实现(版本号字段)

-- 查询时一并获取 version,例如5
UPDATE account SET balance=balance-100, version=version+1 WHERE id=1 AND version=5;
-- 如果该SQL更新数为0,说明数据已被别的线程改过,需要重试

Java实现(CAS 原子类)

import java.util.concurrent.atomic.AtomicInteger;public class OptimisticLockDemo {private AtomicInteger value = new AtomicInteger(0);public void tryUpdate() {int oldVal, newVal;do {oldVal = value.get();newVal = oldVal + 1;} while (!value.compareAndSet(oldVal, newVal));System.out.println("成功更新为:" + newVal);}
}

三、可重入锁(Reentrant Lock)

定义:同一线程多次申请同一把锁不会被阻塞,内部会维护锁“重入次数”。

作用:方便递归、父子方法重复加锁的场景,避免死锁。

1. synchronized的可重入性:

public class ReentrantSyncDemo {public synchronized void outer() {System.out.println("outer....");inner();}public synchronized void inner() {System.out.println("inner....");}public static void main(String[] args) {new ReentrantSyncDemo().outer();}
}

2. ReentrantLock的可重入性:

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockDemo {private final ReentrantLock lock = new ReentrantLock();public void methodA() {lock.lock();try {System.out.println("进入methodA");methodB();} finally {lock.unlock();}}public void methodB() {lock.lock();try {System.out.println("进入methodB");} finally {lock.unlock();}}
}

四、锁的升级(锁的优化机制)

定义:JVM对synchronized的性能优化,锁的状态会随竞争程度自动“升级”,包括偏向锁、轻量级锁和重量级锁。

锁的状态与升级流程

  • 无锁:对象未被线程持有。
  • 偏向锁:假设只有一条线程反复获得锁,极高性能(无任何同步原语)。
  • 轻量级锁:短期竞争时的自旋锁(无需阻塞,性能高)。
  • 重量级锁:竞争激烈时的传统监视器锁(阻塞/唤醒)。

状态升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
(注意:不会降级)

代码观察(JDK源码、JOL工具辅助理解):

public class LockUpgradeDemo {public static void main(String[] args) throws Exception {Object obj = new Object();synchronized (obj) {// 查看对象头信息System.out.println("进入同步块");}}
}

使用JOL(Java Object Layout)库可以详细观察对象头信息及锁状态变化。


五、总结对比表

类型定义场景代码说明优缺点
公平锁先到先得,排队获取锁银行排队new ReentrantLock(true)避免饿死但慢
非公平锁不按顺序,允许插队绝大多数并发场景new ReentrantLock(false)synchronized性能高风险饿死
悲观锁先锁后用,强制同步核心交易、资金synchronized/数据库for update安全性能一般
乐观锁无锁冲突检测后重试高频读、低冲突Java原子类、版本号字段快,不适用多冲突
可重入锁自身可多次获得同一锁递归与多调用场景synchronizedReentrantLock灵活无死锁
锁的升级JVM智能优化锁粒度JVM自动控制synchronized(自动实现)性能最优动态选择

六、开发实践

  • 公平锁用于严格保证顺序,通常业务场景非必须;
  • 乐观锁适用于冲突概率小、性能要求高的场合;
  • 悲观锁安全但性能低,不适于高并发的热点资源;
  • 可重入锁极大提高编码灵活性,避免递归死锁;
  • JVM锁优化机制让大部分场合无需人工干涉锁粒度;
  • 项目开发建议优先选择简单的synchronized,高并发场景评估使用ReentrantLock及乐观锁。

http://www.dtcms.com/a/297228.html

相关文章:

  • MJ11032G和MJ11033G是对管由onsemi/安森美公司研发的一款高性能、低功耗的达林顿晶体管
  • 能源管理网页的碳中和视觉语言:数据图表中的环保色彩体系创新
  • 基于51单片机的光照强度检测系统Protues仿真设计
  • 卸油管连接检测误报率↓78%:陌讯多模态融合算法实战解析
  • 信息学奥赛一本通 1593:【例 2】牧场的安排 | 洛谷 P1879 [USACO06NOV] Corn Fields G
  • 机器学习——KNN算法
  • SpringBoot与ApacheSpark、MyBatis实战整合
  • DeepSeek FlashMLA 技术拆解,AI 推理迎来颠覆性突破
  • 黑马点评常见面试题
  • Apache Ranger 权限管理
  • Python之--字典
  • CMake进阶: 检查函数/符号存在性、检查类型/关键字/表达式有效性和检查编译器特性
  • LP-MSPM0G3507学习--11ADC之二双通道高速DMA采样
  • rtpengine的docker化
  • Linux进程信号——信号保存
  • 在幸狐RV1106板子上用gcc14.2本地编译安装ssh客户端/服务器、vim编辑器、sl和vsftpd服务器
  • OSI 七层模型和五层模型
  • Vue3 学习教程,从入门到精通,Vue3 监听属性(Watchers)语法知识点及案例代码(16)
  • Unity编辑器拓展 IMGUI与部分Utility知识总结(代码+思维导图)
  • JAVA-09(2025.07.25学习记录)
  • MMRotate ReDet ReFPN 报错 `assert input.type == self.in_type`
  • Franky — 边缘计算智能语音助手 / Edge‑Computing Smart Voice Assistant
  • 04-netty基础-Reactor三种模型
  • docker compose xtify-music-web
  • 华为OpenStack架构学习9篇 连载—— 02 OpenStack界面管理【附全文阅读】
  • VR 三维重建:重塑建筑工程全生命周期的数字化革命
  • [NLP]多电源域设计的仿真验证方法
  • Redis 5.0.14安装教程
  • Android 10.0 sts CtsSecurityBulletinHostTestCases的相关异常分析
  • 关于自定义域和 GitHub Pages(Windows)