开发过程中,有时候为了解决多线程竞争问题需要加锁,通常锁定的对象是class,object,method,但在特定时候我们需要更细粒度的加锁,也就是根据不同输入参数来锁定不同的资源,这样只有调用此方法的不同线程传参一样才会进行竞争。

比如一个简单的例子:假设系统为用户提供借款,每月有个限额。每月的借款记录都记在transaction_detail表中,此时当一个用户需要进行借款的时候,我们需要进行一下操作:

method:borrow_money

1、判断用户是否达到借款限额,本月已借款量:select sum(amount) from transaction_detail where user_id=XX;

2、其他业务处理

3、插入transaction_detail

但是以上三步并非原子的,也就是假设用户限额为3w,已借款量1.5w(还可借1.5w),现在有两个线程a、b,每个线程都需要借款1w。

当线程a执行完第一步判断,此时线程a挂起。

线程2开始执行,由于此时借款1w<可借的1.5w,于是线程2执行下去成功贷款1w,插如新的贷款记录。

a线程此时继续执行2、3步也成功贷款了1w,这样该用户当月贷款量已达3.5w大于总限额了(违背了每月3w的限额)。导致这个情况就是以上3步非原子的,在我们执行借款、插入借款记录的贷款状态很可能与我们判断当月已贷款量已经不同了。

这样我们就需要对上面的三步进行加锁同步,但是如果我们将上诉方法borrow_money使用synchronized,将会引发很严重的效率问题,因为这样整个系统的所有线程在执行borrow_money都是竞争关系的,这样就会造成系统性能瓶颈。

于是我们可以使用一种更细粒度的加锁机制。可以对特定字符串加锁来保证操作的原子性同时减少线程对资源的竞争,这个特定字符串需要与用户一对一的关联,同时确保不会影响系统其它操作,所以可以使用:特殊字符串(确保字符串具有特殊性)  + id。

而且我们的字符串需要到jvm的常量池中获取这样确保对于相同的用户获取的字符串是一个对象。

String syncKey = ("borrow_money_" + userId).intern();
synchronized (syncKey) {
  
  //1、判断用户本月已借款量:select sum(amount) from transaction_detail where user_id=XX;   //2、其他业务处理   //3、插入transaction_detail }

如上便解决对于同一个key 独占访问,但这仅限于同一个jvm,如果服务器部署在集群上便无法达到预期效果。

此时可以将每月总借款信息记录在一张表中,这样在判断与操作的时候可以根据这条记录来判断状态

或者使用分布式的锁,如:ZooKeeper进行管理。

Java业务原子性的一种实现(key 独占访问)的更多相关文章

  1. Java分布式锁的三种实现方案(redis)

    方案一:数据库乐观锁 乐观锁通常实现基于数据版本(version)的记录机制实现的,比如有一张红包表(t_bonus),有一个字段(left_count)记录礼物的剩余个数,用户每领取一个奖品,对应的 ...

  2. java多线程中的三种特性

    java多线程中的三种特性 原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并 ...

  3. Java开发中的23种设计模式(转)

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  4. java 遍历Map的四种方式

      java 遍历Map的四种方式 CreationTime--2018年7月16日16点15分 Author:Marydon 一.迭代key&value 第一种方式:迭代entrySet 1 ...

  5. 【转载】Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式…)介绍

    转载地址:http://blog.csdn.net/truong/article/details/46711045 关键字:Redis的Java客户端Jedis的八种调用方式(事务.管道.分布式…)介 ...

  6. Java创建线程的四种方式

    Java创建线程的四种方式 1.继承Thread类创建线程 定义Thread类的子类,并重写该类的run方法,run()方法的内容就是该线程执行的内容 创建Thread子类的实例,即创建了线程对象. ...

  7. Java避坑宝典《Java业务开发常见错误100例》上线了

    写这个专栏的缘起 之前我写过一篇博客:<朱晔的互联网架构实践心得S2E2:写业务代码最容易掉的10种坑>,引起的关注还是挺多的.后来和极客时间的编辑一拍即合决定以这个为题写一个专栏.其实所 ...

  8. Java多线程--原子性、可见性、有序性

    计算机的内存模型: 计算机在运行行程序的时候,指令由CPU执行,计算机上数据存放在物理内存当中,CPU在执行指令的时候免不了要和数据打交道.刚开始,还相安无事的,但是随着CPU技术的发展,CPU的执行 ...

  9. Springboot Jpa: [mysql] java.sql.SQLException: Duplicate entry 'XXX' for key 'PRIMARY'

    前言 1.问题背景 偶尔会出现登录请求出错的情况,一旦失败就会短时间内再也登录不上,更换浏览器或者刷新可能会暂时解决这个问题. 项目运行日志如下: 2022-07-21 09:43:40.946 DE ...

随机推荐

  1. 白学jquery Mobile《构建跨平台APP:jQuery Mobile移动应用实战》连续7-电话问卷调查

    [例7-3  文本编辑框创建一个简单的调查问卷] 01     <!DOCTYPEhtml> 02     <html> 03     <head> 04     ...

  2. Redis测井系统

    什么是 SLOWLOG Slow log 是 Redis 用来记录查询运行时间的日志系统. 查询运行时间指的是不包含像client响应(talking).发送回复等 IO 操作,而单单是运行一个查询命 ...

  3. 【Java收集的源代码分析】Hashtable源代码分析

    Hashtable简单介绍 Hashtable相同是基于哈希表实现的,相同每一个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时.相同会自己主动增长. Has ...

  4. overflow的几个坑

    在android 4.0的原生浏览器上注意: html元素上不要加overflow: auto;的样式,否则会造成有些元素无法点击 在absolute元素上 不要加 overflow: auto; 否 ...

  5. Swing 组件焦点设置

    在Swing中,焦点默认是在第一个组件上,所以在项目中想将焦点设置在其他的组件上,如JTextField!但通过requestFocus()方法不起作用,有人提供以下解决方法: 全部初始化之后,jTe ...

  6. NuttX 介绍

    (嵌入式 实时操作系统 rtos nuttx 7.1) NuttX 介绍 转载请注明出处:http://blog.csdn.net/zhumaill/article/details/24197637 ...

  7. Android - 除首次使用状态(SharedPreferences)

    除首次使用状态(SharedPreferences) 本文地址: http://blog.csdn.net/caroline_wendy 用户首次登陆时, 可能须要用户教育, 解说界面操作, 可是不应 ...

  8. JTextArea Demo

    在往JTextArea中填充数据时,JTextArea上的滚动条也可以拖动.解决办法:主线程放在EDT中,fill JTextArea的操作放在另外一个线程中,这样fill操作与GUI上的操作就分离了 ...

  9. role &#39;PLUSTRACE&#39; does not exist

    I have created a new user named watson and granted the related priviledges as following: SQL> cre ...

  10. 解决一bug的流程复盘

    听同事说有一个功能不好使了,当时有事,过了一段时间来看看这个bug 解决问题时,看的是老的日志,根据老日志看来看去没有发现问题,觉得很困惑 然后手动执行了一下,发现问题没有重现.与另一个团队的同事沟通 ...