在转账操作中,一致性必须要保证的,转账的前后,各个账户的金额必须符合算术一致性

如何保证一致性? 引入事务机制是肯定的,但是这就够了么,下面我们对其进行测试

场景:

小明账户中有10000元,小强账户中有10000元,小红账户中有0元

有三个操作同时进行:小明给小红转账10元 ,小强给小红转账10元,小红自己往账户存入1元

为了模拟高并发,我这里将上述操作复制300份,每份300个线程中同时执行

测试一:

代码如下:(测试代码,细节的判断我就省略了)

这里仅仅引入了事务机制,事务的隔离级别为 REPEATABLE_READ

/**

* 转账10元

*/

@Transactional

@Override

public void testTransfer(Integer idFrom, Integer idTo) {

//SQL语句 : select * from users where id=?

Users from = usersMapper.selectById(idFrom);

Users to = usersMapper.selectById(idTo);

System.out.println(from.getUserName() + "向" + to.getUserName() + "转账 ");

//这里加上10毫秒的延时,用于测试

delay(10L);

//转账方减10元

from.setMoney(from.getMoney() - 10);

usersMapper.updateByPrimaryKey(from);

//这里加上10毫秒的延时,用于测试

delay(10L);

//收款方增加10元

to.setMoney(to.getMoney() + 10);

usersMapper.updateByPrimaryKey(to);

System.out.println(from.getUserName() + "向" + to.getUserName() + "转账完毕---");

}

/**

* 存入1元

*

* @param id

*/

@Override

@Transactional

public void testDeposit(Integer id) {

System.out.println("小红自己存钱");

Users user = usersMapper.selectById(id);

//延时10ms

delay(10L);

//存入1元

user.setMoney(user.getMoney() + 1);

usersMapper.updateByPrimaryKey(user);

System.out.println("小红自己存钱 完毕+++");

}

/**

* 延时

*/

public void delay(Long ms) {

try {

Thread.sleep(ms);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

测试内容

测试

同时开启900个线程

其中300个线程: 小明向小红转账10元

其中300个线程:小强向小红转账10元

其中300个线程:小红自己存入1元

数据库初始值:

期望值:

测试开始,转账中.......

3次测试的结果为:

分析:很明显这里是由于多线程操作同一数据遇到的线程同步安全问题,怎么解决呢?第一时间我想到的是加个同步锁:synchronization,加锁后进行第二次测试

测试二:

代码如下: 在测试一的基础上,加了同步锁synchronized

/**

* 转账10元

*/

@Transactional

@Override

public synchronized void testTransfer(Integer idFrom, Integer idTo) {

//SQL语句 : select * from users where id=?

Users from = usersMapper.selectById(idFrom);

Users to = usersMapper.selectById(idTo);

System.out.println(from.getUserName() + "向" + to.getUserName() + "转账 ");

//这里加上10毫秒的延时,用于测试

delay(3L);

//转账方减10元

from.setMoney(from.getMoney() - 10);

usersMapper.updateByPrimaryKey(from);

//这里加上10毫秒的延时,用于测试

delay(3L);

//收款方增加10元

to.setMoney(to.getMoney() + 10);

usersMapper.updateByPrimaryKey(to);

System.out.println(from.getUserName() + "向" + to.getUserName() + "转账完毕---");

}

/**

* 存入1元

*

* @param id

*/

@Override

@Transactional

public synchronized void testDeposit(Integer id) {

System.out.println("小红自己存钱");

Users user = usersMapper.selectById(id);

//延时10ms

delay(3L);

//存入1元

user.setMoney(user.getMoney() + 1);

usersMapper.updateByPrimaryKey(user);

System.out.println("小红自己存钱 完毕+++");

}

/**

* 延时

*/

public void delay(Long ms) {

try {

Thread.sleep(ms);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

测试内容

测试

同时开启900个线程

其中300个线程: 小明向小红转账10元

其中300个线程:小强向小红转账10元

其中300个线程:小红自己存入1元

数据库初始值:

期望值:

测试开始,转账中.......

测试结果:

分析:用时增加到17秒,结果还是会出错,这时为啥呢? 由于上面的同步锁只锁了自己的方法,但是其他方法还是可以操作数据库的,有没有一种方法是给数据库的某条数据上锁呢,有的,这里可以使用悲观锁:顾名思义,就是很悲观的锁,事务在查询数据时,总是悲观的认为其它事务会操作这条数据,所以上了锁。直到事务commit才会释放。期间会阻塞其他事务想要给这条数据上锁。下面进行悲观锁测试。

测试3:

这里去掉了synchronization修饰符,使用悲观锁

在查询语句中添加悲观锁:

/**

* 转账10元

*/

@Transactional

@Override

public void testTransfer(Integer idFrom, Integer idTo) {

//SQL语句 : select * from users where id=? for updata

Users from = usersMapper.selectByIdForUpdate(idFrom);

Users to = usersMapper.selectByIdForUpdate(idTo);

System.out.println(from.getUserName() + "向" + to.getUserName() + "转账 ");

//这里加上10毫秒的延时,用于测试

delay(10L);

//转账方减10元

from.setMoney(from.getMoney() - 10);

usersMapper.updateByPrimaryKey(from);

//这里加上10毫秒的延时,用于测试

delay(10L);

//收款方增加10元

to.setMoney(to.getMoney() + 10);

usersMapper.updateByPrimaryKey(to);

System.out.println(from.getUserName() + "向" + to.getUserName() + "转账完毕---");

}

/**

* 存入1元

*

* @param id

*/

@Override

@Transactional

public void testDeposit(Integer id) {

System.out.println("小红自己存钱");

Users user = usersMapper.selectByIdForUpdate(id);

//延时10ms

delay(10L);

//存入1元

user.setMoney(user.getMoney() + 1);

usersMapper.updateByPrimaryKey(user);

System.out.println("小红自己存钱 完毕+++");

}

/**

* 延时

*/

public void delay(Long ms) {

try {

Thread.sleep(ms);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

这里将数据量增加50倍测试:

测试内容

测试

循环50次执行以下操作:

同时开启900个线程

其中300个线程: 小明向小红转账10元

其中300个线程:小强向小红转账10元

其中300个线程:小红自己存入1元

数据库初始值:

期望值:

测试开始,转账中.......

测试结果:

用时13分钟

测试成功

总结:

对于数据库的多线程操作,需要加数据库级别的锁,悲观锁可以实现此功能,稍后测试乐观锁...

java 转账 锁_java 关于高并发下的银行转账问题相关推荐

  1. java 转账 锁_Java多线程 多个人转账发生死锁

    Java多线程 多个人转账发生死锁 Java多线程 多个人转账发生死锁 文章目录多个人转账发生死锁 多个人转账发生死锁 人数多的时候, 依然会发生死锁, 遵循墨菲定律. 虽然人多的时候发生死锁的几率不 ...

  2. java细粒度锁_Java细粒度锁实现的3种方式

    最近在工作上碰见了一些高并发的场景需要加锁来保证业务逻辑的正确性,并且要求加锁后性能不能受到太大的影响.初步的想法是通过数据的时间戳,id等关键字来加锁,从而保证不同类型数据处理的并发性.而java自 ...

  3. java 代码锁_Java 锁的知识总结及实例代码

    java中有哪些锁 这个问题在我看了一遍后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式是要带着问题去学,并且解决问题. ...

  4. java线程 锁_Java多线程(二) 多线程的锁机制

    当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...

  5. java耗时操作阻塞_springboot~高并发下耗时操作的实现

    高并发下的耗时操作 官方文档中说DeferredResult和Callable都是为了异步生成返回值提供基本的支持.简单来说就是一个请求进来,如果你使用了DeferredResult或者Callabl ...

  6. java 并发锁_Java并发教程–锁定:内在锁

    java 并发锁 在之前的文章中,我们回顾了在不同线程之间共享数据的一些主要风险(例如原子性和可见性 )以及如何设计类以安全地共享( 线程安全的设计 ). 但是,在许多情况下,我们将需要共享可变数据, ...

  7. java 并发锁_Java并发教程–重入锁

    java 并发锁 Java的synced关键字是一个很棒的工具–它使我们可以通过一种简单可靠的方式来同步对关键部分的访问,而且也不难理解. 但是有时我们需要对同步进行更多控制. 我们要么需要分别控制访 ...

  8. JAVA random 缺陷_Random在高并发下的缺陷以及JUC对其的优化

    Random可以说是每个开发都知道,而且都用的很6的类,如果你说,你没有用过Random,也不知道Random是什么鬼,那么你也不会来到这个技术类型的社区,也看不到我的博客了.但并不是每个人都知道Ra ...

  9. java 下单 锁_JAVA 高并发下单解决方案-分布式锁

    背景:高并发情况下,商品出现超卖的情况. 最终目标:保证数据的最终一致性. Contrrler 层框架 : Spring MVC 第一次尝试:最初的时候,发现Spring MVC是一个单例多线程的Co ...

最新文章

  1. CNN笔记:通俗理解卷积神经网络
  2. HTML5和CSS3不仅仅是两项新的Web技术标准
  3. CentOS 7.4 基于LNMP搭建wordpress
  4. mysql数据记录更新版本问题_MySQL版本升级遇到的问题小结
  5. mysql sql len_MySQL的查询计划中ken_len的值计算方法
  6. Linux目录遍历实现,列出目录下文件,可使用部分参数
  7. vue+node实现中间层同步调用接口
  8. 竞争者都是 飞鸽传书 高手
  9. python array函数_Python bytearray() 函数
  10. linux运行c程序a. out,无法运行已编译的文件 – bash:./ a.out:权限被拒绝. (我试过chmod)...
  11. 功能测试-测试定义与原则
  12. 蓝桥杯 ADV-225 算法提高 9-2 文本加密
  13. Leetcode 456.132模式
  14. 传微软PK谷歌 将于2014年推出智能眼镜
  15. python读取npy文件_python – 如何在磁盘上创建一个numpy .npy文件?
  16. FDDB人脸数据集dataset的dataset数据集的制作
  17. matlab中输入数学符号,matlab 数学符号输入
  18. Android实现推送PushService通知Notification
  19. windows快速清理垃圾文件bat
  20. 学会ipad当作电脑扩展屏方法

热门文章

  1. CAD与Gerber的差别有哪些?带你了解一下
  2. Shader-FresnelReflection(菲涅尔反射)
  3. LrcView逐行歌词
  4. 一款好用的pdf转word工具
  5. 基于北洋OPOS SDK二次开发包,支持EPSON和北洋、佳博、商祺等支持标准ESC/POS指令的POS打印机
  6. 【打卡】求直角坐标系内两点间距离
  7. 使用拉易网制作图文并茂的精美HTML邮件模板(终)
  8. 反洗钱检验java_从Drools规则引擎到风控反洗钱系统V0.2.3.pdf
  9. 还是关于前端性能优化,真的是的从细节开始
  10. 服务器的固态硬盘使用raid非ssd,在VMware ESXi中使用固态硬盘的注意事项