开发过程中,有时候为了解决多线程竞争问题需要加锁,通常锁定的对象是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. 【C语言探索之旅】 第二部分第三课:数组

    内容简介 1.课程大纲 2.第二部分第三课: 数组 3.第二部分第四课预告:字符串 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三个游戏. C语 ...

  2. 使用Python做科学计算初探(转)

    今天在搞定Django框架的blog搭建后,尝试一下python的科学计算能力. python的科学计算有三剑客:numpy,scipy,matplotlib. numpy负责数值计算,矩阵操作等: ...

  3. 在SQL2008中,如何让id自动生成并自动递增?如何让时间默认生成?

    id自动递增:    如果是用语句操作,这样定义:ID INT IDENTITY,如果是要生成一对数字,这样定义:ID INT IDENTITY(1,1)    如果要在SQL Server的表中设置 ...

  4. JDNI

    JNDI是为了一个最最核心的问题:是为了解耦,是为了开发出更加可维护.可扩展的系统JNDI和JDBC起的作用类似:JDBC(Java Data Base Connectivity,java数据库连接) ...

  5. Java - 面向对象(object oriented)计划 详细解释

    面向对象(object oriented)计划 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/24058107 程序包括 ...

  6. quick-cocos2d-x游戏开发【3】——display.newSprite创建向导

    游戏嘛.没有图片没有图片可以称为你的游戏,所以,我们看一下使用quick如何创建精灵的方式. quick的api精灵族的创造仍然是非常具体的解释.因此,建立非常easy. display.newSpr ...

  7. javascript 模块化编程 1

    var myModule=(function(){ var models={}; function define(name,deps,cb){ var tk=[]; for(var i=0,l=dep ...

  8. 为大型数据文件每行只能产生id

    为大型数据文件每行只能产生id 4个主要思路: 1 单线程处理 2 普通多线程 3 hive 4 Hadoop 搜到一些參考资料 <Hadoop实战>的笔记-2.Hadoop输入与输出 h ...

  9. DeviceIoControl的使用说明

    应用程序和驱动程序的通信过程是:应用程序使用CreateFile函数打开设备,然后用DeviceIoControl与驱动程序进行通信,包含读和写两种操作.还能够用ReadFile读数据用WriteFi ...

  10. Javascript入门视频教程

    1,第一节 http://pan.baidu.com/play/video#video/path=%2F%E6%95%99%E5%AD%A61.mov&t=-1 2,第二节 http://pa ...