读者朋友,下午好!

今天分享一个很好地讲解并发中竞争条件的例子——银行在多个线程时候,随机在2个账户之间随机的转金额,在未加锁的时候,账户总金额会出乎意料的不一致;我们希望的是无论怎么转账,银行所有账户的总金额是固定不变的。

示例代码来源

《Java核心技术 卷1 第10版》 Core Java Volume I-Fundamentals(10th Edition)
[美] Cay S.Horstmann 著
周立新 陈波 叶乃文 邝劲筠 杜永萍 译

代码库:

git@github.com:cmhhcm/guiAndConcurrent.git

一、银行转账示例

Bank

package com.cmh.concurrent.unsynch;import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** Author: 起舞的日子* Date:2021/4/18 3:08 下午*/
public class Bank {private final double[] accounts;private Lock bankLock = new ReentrantLock();/*** 初始化银行** @param n              the number of accounts* @param initialBalance the initial balance of each account*/public Bank(int n, double initialBalance) {accounts = new double[n];Arrays.fill(accounts, initialBalance);}/*** 从一个账户给另一个账户转账** @param from* @param to* @param amount*/public void transfer(int from, int to, double amount) {bankLock.lock();try {if (accounts[from] < amount) {return;}System.out.print(Thread.currentThread());accounts[from] -= amount;System.out.printf(" %10.2f from %d to %d", amount, from, to);accounts[to] += amount;System.out.printf("  Total Balance: %10.2f %n", getTotalBalance());System.out.println();} finally {bankLock.unlock();}}/*** 来看一下transfer这个方法字节码指令执行情况* javac Bank.java* javap -c -v Bank* <p>* 之后看到的是这样的:* 大体找到对应accounts[from] -= amount的指令:* 22: getfield      #7                  // Field accounts:[D 去from索引位置获取到这个值* 25: iload_1     将第二个int类型的值推送至栈顶* 26: dup2        复制栈顶的数值并将复制值压入栈顶* 27: daload      将double数组指定索引的值推送至栈顶* 28: dload_3     将第四个double型本地变量推送至栈顶* 29: dsub        将栈顶两double型数值相减并将结果压入栈顶* 30: dastore     将栈顶double型数值存入指定数组指定索引的位置* <p>* 通过以上指令,基本知道在accounts[from] = accounts[from] - amount的时候,* 至少需要压栈、详减、存入几个指令,那么在这个过程中,未做并发处理,就会有并发问题。*/public double getTotalBalance() {double sum = Arrays.stream(accounts).sum();return sum;}public int size() {return accounts.length;}
}

BankTest

package com.cmh.concurrent.unsynch;/*** This program shows data corruption when multiple threads access a data structure* <p>* Author: 起舞的日子* Date:2021/4/18 3:08 下午*/
public class UnsynchBankTest {public static final int NACCOUNTS = 100;public static final double INITIAL_BALANCE = 1000;public static final double MAX_ACCOUNT = 1000;public static final int DELAY = 10;public static void main(String[] args) {Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);for (int i = 0; i < NACCOUNTS; i++) {int fromAccount = i;Runnable runnable = () -> {try {while (true) {int toAccount = (int) (bank.size() * Math.random());double amount = MAX_ACCOUNT * Math.random();bank.transfer(fromAccount, toAccount, amount);Thread.sleep((int) (DELAY * Math.random()));}} catch (InterruptedException interruptedException) {interruptedException.printStackTrace();}};Thread thread = new Thread(runnable);thread.start();}}}

二、加锁核心代码

未加锁前运行效果:

加锁后运行效果:

三、原理分析

1、为什么会出现总金额不一致的情况?

因为accounts[from] = accounts[from] - amount的时候,
背后的JVM指令不是一个原子性操作,即是一行代码,背后是分几步来完成的。那么在这几步的过程中,就可能被别的线程“抢占”了(操作系统的分配规则)。

通过javap 可以查看编译后的Bank.class文件的这行代码的执行步骤:Bank类中已做详细注释说明。这里在重点讲一下查看流程:

 第一步,编译Bank.java javac Bank.java 第二步:javap -c -v Bank
即可查看详细指令。-c -v详细含义见下图-

好了,看一下核心执行逻辑:

2、加锁怎么加?为什么用公平锁?

待后续补充

好了,再会!

并发-2-竞争条件例子、锁对象相关推荐

  1. java 锁竞争_Java多线程中的竞争条件、锁以及同步的概念

    竞争条件 1.竞争条件: 在java多线程中,当两个或以上的线程对同一个数据进行操作的时候,可能会产生"竞争条件"的现象.这种现象产生的根本原因是因为多个线程在对同一个数据进行操作 ...

  2. Linux并发与竞争介绍(原子操作、自旋锁、信号量、互斥体)

    目录 并发与竞争 并发与竞争简介 保护内容是什么 原子操作 原子操作简介 原子整形操作API函数(atomic_t 结构体) 原子位操作API 函数 自旋锁 自旋锁简介 自旋锁API函数 线程与线程 ...

  3. 023 Rust死灵书之并发、竞争

    介绍 本系列录制的视频主要放在B站上Rust死灵书学习视频 Rust 死灵书相关的源码资料在https://github.com/anonymousGiga/Rustonomicon-Source 并 ...

  4. java线程同步——竞争条件的荔枝+锁对象

    [0]README 0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java线程同步--竞争条件的荔枝+锁对象 的相关知识: 0.2) for full sou ...

  5. java 并发的原子性_Java并发教程–原子性和竞争条件

    java 并发的原子性 原子性是多线程程序中的关键概念之一. 我们说一组动作是原子的,如果它们都以不可分割的方式作为单个操作执行. 认为多线程程序中的一组操作将被串行执行是理所当然的,可能会导致错误的 ...

  6. Go 并发 | 数据竞争及竞争条件

    目录 Go 并发 | 数据竞争及竞争条件 数据竞争 (data race) 避免数据竞争的发生 竞争条件 (race condition) 总结 参考 Go 并发 | 数据竞争及竞争条件 Go 并发中 ...

  7. Java并发教程–原子性和竞争条件

    原子性是多线程程序中的关键概念之一. 我们说一组动作是原子的,如果它们都以不可分割的方式作为一个单一的操作执行. 认为多线程程序中的一组操作将被串行执行是理所当然的,可能会导致错误的结果. 原因是由于 ...

  8. 多线程并发中什么是竞争条件?

    跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理. 作者简介:笔名seaboat,擅长工程算法.人工智能算法.自然语言处理.计算机视觉.架构.分布式.高并发.大数据和搜索 ...

  9. linux 信号量 自旋锁 测试 实验,「正点原子Linux连载」第四十八章Linux并发与竞争实验...

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 第四十八章Linux并发与竞争实验 在上 ...

最新文章

  1. 解决:angularjs radio默认选中失效问题
  2. 教你如何一步步将项目部署到Github
  3. Java 常用类 -Arrays
  4. 聊聊微服务的隔离和熔断
  5. linux——shell 中常用的控制语句 for、while、if、case、expect、exit、break、continue
  6. BITPOS key bit [start] [end]
  7. rust go java 性能_Java,Go和Rust之间的比较 - Dexter
  8. linux电脑系统投到电视,教你如何在Linux操作系统下观看电视节目
  9. 为什么数字中台是企业应用新基建?
  10. 载入内存,让程序运行起来
  11. 数据库:跨数据库,服务器数据迁移
  12. windows安装python
  13. Ceres-Solver
  14. 51单片机定时器/计数器
  15. 美团点评2017校园招聘编程题--取红包
  16. 仿网易新闻顶部菜单html,iOS仿网易新闻滚动导航条效果
  17. 0018 求球的表面积和体积
  18. 最新的百度网盘不限速下载工具 - 100兆速度理论10m/s
  19. cFosSpeed(网络加速器软件)官方中文版V11.10.2483 | 网络优化大师 | 绝佳的电脑网络延迟解决方法
  20. bitbake中bb文件的描述

热门文章

  1. 1.2 CSS选择器和布局
  2. html 手机登陆验证码,手机号登录(验证码).html
  3. win10 家庭中文版打开本地组策略编辑器
  4. 【云原生】什么是云原生?如何学习云原生?一篇文章带你了解云原生
  5. 【ida】IDA工具常见利用
  6. 野人与传教士——宽度优先搜索(完整报告,含全部代码)
  7. HIP HOP 街舞文化
  8. 川土微电子|CA-IS1200G全差分隔离运放ADC简介
  9. php生成动态笔画字体,怎么制作手写文字的动画效果视频 文字一笔一划写出来的动画效果制作...
  10. 查看创表语句 SHOW CREATE TABLE t_idcard