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

Java synchronized 锁机制深度解析与实战指南 - 银行转账案例

在这里插入图片描述

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》专栏19年编写主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
🌛《开源项目》本专栏主要介绍目前热门的开源项目,带大家快速了解并轻松上手使用
🍎 《前端技术》专栏以实战为主介绍日常开发中前端应用的一些功能以及技巧,均附有完整的代码示例
✨《开发技巧》本专栏包含了各种系统的设计原理以及注意事项,并分享一些日常开发的功能小技巧
💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整
👍《Spring Security》专栏中我们将逐步深入Spring Security的各个技术细节,带你从入门到精通,全面掌握这一安全技术
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

Java synchronized 锁机制深度解析与实战指南 - 银行转账案例

  • 1.前言
  • 2. synchronized 的三种使用方式
      • 2.1 同步实例方法
      • 2.2 同步静态方法
      • 2.3 同步代码块
  • 3. synchronized 底层实现原理
      • 3.1 字节码层面分析
      • 3.2 锁升级过程(JDK 1.6+优化)
  • 4. 完整实战代码示例
      • 4.1 银行账户转账案例
      • 4.2 多线程测试类
  • 5. synchronized 的高级特性
      • 5.1 可重入性
      • 5.2 等待/通知机制
  • 6. synchronized 最佳实践
  • 7. synchronized 与 Lock 的对比
  • 8. 总结

1.前言

Java 中,synchronized 是最基本且广泛使用的同步原语,用于保证线程对共享资源的互斥访问。它不仅提供了同步性,还通过内存屏障保证了可见性和有序性。

在这里插入图片描述

Synchronized 使用的是每个 Java 对象都内置的 “监视器锁”(Monitor Lock,又称 Intrinsic Lock)。每次进入同步方法或同步块时,线程会获取该对象的监视器锁;退出时释放锁,无论正常结束还是异常退出

SynchronizedJava 中实现线程同步的核心机制,它提供了以下关键功能:

  • 原子性保证:确保临界区代码的不可分割执行
  • 内存可见性:强制线程从主内存读取最新数据,保证变量修改的可见性
  • 有序性:防止编译器和处理器对代码进行重排序

小伙伴们可以通过本片文章,让你测底了解 synchronized 锁机制。


2. synchronized 的三种使用方式

2.1 同步实例方法

锁对象为当前实例 (this),适用场景:对象内的状态保护

public class Counter {private int count = 0;// 同步实例方法public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}

2.2 同步静态方法

锁对象为当前类的 Class 对象,适用场景:类级别的全局状态保护

public class GlobalCounter {private static int count = 0;// 同步静态方法public static synchronized void increment() {count++;}public static synchronized int getCount() {return count;}
}

2.3 同步代码块

锁对象为指定的引用对象,细粒度锁控制,减少锁竞争

public class FineGrainedLock {private final Object lock1 = new Object();private final Object lock2 = new Object();private int value1 = 0;private int value2 = 0;public void incrementValue1() {synchronized(lock1) {value1++;}}public void incrementValue2() {synchronized(lock2) {value2++;}}
}

3. synchronized 底层实现原理

3.1 字节码层面分析

编译器将同步代码块翻译为 monitorentermonitorexit 指令;同步方法则通过标记方法的 access_flags 来识别为同步方法,执行过程和同步块类似

public void synchronizedMethod();Code:0: aload_01: dup2: astore_13: monitorenter   // 进入同步块4: aload_05: dup6: getfield      #2  // Field count:I9: iconst_110: iadd11: putfield      #2  // Field count:I14: aload_115: monitorexit    // 正常退出同步块16: goto          2419: astore_220: aload_121: monitorexit    // 异常退出同步块22: aload_223: athrow24: return

3.2 锁升级过程(JDK 1.6+优化)

无锁 → 偏向锁 → 轻量级锁 → 重量级锁
  • 偏向锁:减少无竞争时的开销
  • 轻量级锁:使用CAS自旋避免线程阻塞
  • 重量级锁:操作系统级别的互斥锁(Mutex Lock)

4. 完整实战代码示例

4.1 银行账户转账案例

下面我们通过经典的银行转账案例来了解 synchronized

public class BankAccount {private final String accountId;private double balance;public BankAccount(String accountId, double balance) {this.accountId = accountId;this.balance = balance;}// 同步方法实现public synchronized void deposit(double amount) {if (amount <= 0) {System.out.println("存款金额必须大于0");return;}balance += amount;System.out.printf("存款成功: 账户 %s 存入 %.2f, 余额: %.2f%n", accountId, amount, balance);}// 同步代码块实现public void withdraw(double amount) {synchronized(this) {if (amount <= 0) {System.out.println("取款金额必须大于0");return;}if (balance < amount) {System.out.printf("取款失败: 账户 %s 余额不足 (%.2f < %.2f)%n", accountId, balance, amount);return;}balance -= amount;System.out.printf("取款成功: 账户 %s 取出 %.2f, 余额: %.2f%n", accountId, amount, balance);}}// 静态同步方法public static synchronized void transfer(BankAccount from, BankAccount to, double amount) {if (from == to) {System.out.println("不能向自己转账");return;}// 使用嵌套同步避免死锁synchronized(from) {synchronized(to) {if (from.balance < amount) {System.out.printf("转账失败: %s 余额不足 (%.2f < %.2f)%n", from.accountId, from.balance, amount);return;}from.balance -= amount;to.balance += amount;System.out.printf("转账成功: %s 向 %s 转账 %.2f%n", from.accountId, to.accountId, amount);}}}public synchronized double getBalance() {return balance;}
}

4.2 多线程测试类

接下来通过多线程来模拟,多个人同时转账的问题

public class BankSimulation {public static void main(String[] args) throws InterruptedException {BankAccount accountA = new BankAccount("A123", 1000);BankAccount accountB = new BankAccount("B456", 2000);// 创建10个存款线程for (int i = 0; i < 10; i++) {new Thread(() -> {for (int j = 0; j < 100; j++) {accountA.deposit(10);try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}// 创建10个取款线程for (int i = 0; i < 10; i++) {new Thread(() -> {for (int j = 0; j < 100; j++) {accountA.withdraw(5);try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}// 创建5个转账线程for (int i = 0; i < 5; i++) {new Thread(() -> {for (int j = 0; j < 50; j++) {BankAccount.transfer(accountB, accountA, 20);try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}// 等待所有线程完成Thread.sleep(5000);System.out.println("\n最终账户余额:");System.out.printf("账户 %s 余额: %.2f%n", accountA.getAccountId(), accountA.getBalance());System.out.printf("账户 %s 余额: %.2f%n", accountB.getAccountId(), accountB.getBalance());}
}

5. synchronized 的高级特性

5.1 可重入性

同一线程可以多次获取同一把锁(锁计数器在 Monitor 中递增)

public class ReentrantExample {public synchronized void methodA() {System.out.println("进入methodA");methodB(); // 调用另一个同步方法}public synchronized void methodB() {System.out.println("进入methodB");}public static void main(String[] args) {ReentrantExample example = new ReentrantExample();example.methodA();}
}

输出结果: 同一个线程可以多次获取同一个锁

进入methodA
进入methodB

5.2 等待/通知机制

wait() 调用会释放锁并加入 Wait Set 等待;
notify()/notifyAll() 唤醒其中等待的线程,使其进入 Entry Set,重新竞争锁

public class WaitNotifyExample {private final Object lock = new Object();private boolean condition = false;public void waitForCondition() throws InterruptedException {synchronized(lock) {while (!condition) {System.out.println("条件不满足,开始等待...");lock.wait(); // 释放锁并等待}System.out.println("条件满足,继续执行");}}public void setCondition() {synchronized(lock) {condition = true;lock.notifyAll(); // 通知所有等待线程System.out.println("已设置条件并通知");}}public static void main(String[] args) throws InterruptedException {WaitNotifyExample example = new WaitNotifyExample();new Thread(() -> {try {example.waitForCondition();} catch (InterruptedException e) {e.printStackTrace();}}).start();Thread.sleep(1000); // 确保等待线程先启动new Thread(() -> {example.setCondition();}).start();}
}

6. synchronized 最佳实践

锁对象选择原则

  • 使用私有final对象:private final Object lock = new Object();
  • 避免使用字符串常量或基础类型包装类
  • 不要使用可能被重用的对象(如Integer缓存)

减少锁竞争策略

// 锁分解:将大锁拆分为多个小锁
public class SeparateLocks {private final Object readLock = new Object();private final Object writeLock = new Object();private int readCount = 0;private int writeCount = 0;public void incrementRead() {synchronized(readLock) {readCount++;}}public void incrementWrite() {synchronized(writeLock) {writeCount++;}}
}// 锁粗化:减少不必要的锁获取/释放
public void processBatch(List<Item> items) {synchronized(this) {// 合并多个小操作为一个锁块for (Item item : items) {processItem(item);}}
}

避免死锁的四种方法

// 1. 固定顺序获取锁
public void transfer(BankAccount from, BankAccount to, double amount) {BankAccount first = from.compareTo(to) < 0 ? from : to;BankAccount second = from.compareTo(to) < 0 ? to : from;synchronized(first) {synchronized(second) {// 转账逻辑}}
}// 2. 使用tryLock(需配合Lock API)
// 3. 设置超时时间
// 4. 死锁检测与恢复

7. synchronized 与 Lock 的对比

特性synchronizedLock(ReentrantLock)
实现机制JVM 内置实现Java API 实现
锁获取方式自动获取释放手动 lock()/unlock()
可中断性不支持支持 lockInterruptibly()
超时获取不支持支持 tryLock(timeout)
公平锁非公平(可设置偏向锁)可选公平/非公平
条件队列单一条件支持多个 Condition
性能JDK6+ 优化后接近 Lock竞争激烈时更优
代码复杂度简单需配合 try-finally 确保释放

选型建议

  • 简单场景优先使用 synchronized
  • 需要高级功能(超时、中断等)时选择 Lock
  • 读写分离场景使用 ReadWriteLock

8. 总结

  • synchronizedJava 提供的内置锁机制,易用且实用。
  • 底层通过监视器锁、对象头与字节码指令实现互斥、可重入及可见性保障。
  • JVM 针对其实现做了多层性能优化。
  • 使用 wait/notify 时要结合 while 保护,避免虚假唤醒。

通过合理使用 synchronized 锁机制,小伙伴们可以在保证线程安全的同时,构建高性能的并发Java应用。在实际项目中,建议结合具体场景选择最合适的同步策略,并持续进行性能监控和优化。

如果你在实践过程中有任何疑问或更好的扩展思路,欢迎在评论区留言,最后希望大家 一键三连 给博主一点点鼓励!


在这里插入图片描述

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

相关文章:

  • 深度学习超参数调优指南
  • Scrapy入门实战指南:从零开始打造高效爬虫系统
  • 每日算法刷题Day45 7.11:leetcode前缀和3道题,用时1h40min
  • 机器学习之线性回归(七)
  • 安全领域的 AI 采用:主要用例和需避免的错误
  • 基于k8s环境下pulsar高可用测试和扩缩容(上)
  • 基于k8s环境下pulsar高可用测试和扩缩容(下)
  • 线程通信与进程通信的区别笔记
  • CMU15445-2024fall-project1踩坑经历
  • 小米汽车今年6月新车交付超2.5万辆
  • Marin说PCB之Allegro高亮BOM器件技巧详解
  • 格密码--数学基础--03格的基本域与行列式
  • 用AI做带货视频评论分析【Datawhale AI 夏令营】
  • 《P3976 [TJOI2015] 旅游》
  • Guava LoadingCache
  • Apipost 与 Apifox 的 API 调试功能:全面对比与深入分析
  • Vue + Element UI 实现选框联动进而动态控制选框必填
  • 牛客 —— JZ22 链表中倒数最后k个结点
  • 前端八股-promise
  • FreeRTOS踩坑小记——vTaskList函数的使用
  • Activiti:activiti-app.war持久化功能实现
  • AI优化器美国VPS集成:智能算力部署与性能调优指南
  • (补充)RS422
  • Mysql:分库分表
  • STM32F103之存储/启动流程
  • 【世纪龙科技】几何G6新能源汽车结构原理教学软件
  • Linux C 管道文件操作
  • [spring6: @EnableLoadTimeWeaving]-使用案例
  • SSH基础原理
  • 速盾:高防CDN和普通CDN的区别大吗?