Java乐观锁的实现原理(案例)
简要说明:
表设计时,需要往表里加一个version字段。每次查询时,查出带有version的数据记录,更新数据时,判断数据库里对应id的记录的version是否和查出的version相同。若相同,则更新数据并把版本号+1;若不同,则说明,该数据发送并发,被别的线程使用了,进行递归操作,再次执行递归方法,知道成功更新数据为止
简单说说乐观锁。乐观锁是相对于悲观锁而言。悲观锁认为,这个线程,发生并发的可能性极大,线程冲突几率大,比较悲观。一般用synchronized实现,保证每次操作数据不会冲突。乐观锁认为,线程冲突可能性小,比较乐观,直接去操作数据,如果发现数据已经被更改(通过版本号控制),则不更新数据,再次去重复 所需操作,知道没有冲突(使用递归算法)。
因为乐观锁使用递归+版本号控制 实现,所以,如果线程冲突几率大,使用乐观锁会重复很多次操作(包括查询数据库),尤其是递归部分逻辑复杂,耗时和耗性能,是低效不合适的,应考虑使用悲观锁。
乐观锁悲观锁的选择:
乐观锁:并发冲突几率小,对应模块递归操作简单 时使用
悲观锁:并发几率大,对应模块操作复杂 时使用
案例一
/**
* 自动派单
* 只查出一条 返回list只是为了和查询接口统一
* 视频审核订单不派送
* @param paramMap
* @return
*/
public List<AutomaticAssignDto> automaticAssign(Map<String, Object> paramMap){
//派送规则
String changeSortSet = RedisCacheUtil.getValue(CACHE_TYPE.APP, "changeSortSet");
if (StringUtils.isBlank(changeSortSet)) {
changeSortSet = customerManager.getDictionaryByCode("changeSortSet");
if (StringUtils.isNotBlank(changeSortSet)) {
RedisCacheUtil.addValue(CACHE_TYPE.APP, "changeSortSet", changeSortSet,30,TimeUnit.DAYS);
} else {
changeSortSet = ConstantsUtil.AssignRule.FIFO; // 默认先进先审
}
}
AutomaticAssignDto automaticAssignDto = new AutomaticAssignDto();
automaticAssignDto.setChangeSortSet(changeSortSet);
automaticAssignDto.setUserTeam(CommonUtils.getValue(paramMap, "userTeam"));
List<AutomaticAssignDto> waitCheckList = automaticAssignMybatisDao.automaticAssignOrder(automaticAssignDto);
if(waitCheckList != null && waitCheckList.size()>0){
automaticAssignDto = waitCheckList.get(0);
automaticAssignDto.setSendStatus(ConstantsUtil.SendStatus.SEND);
automaticAssignDto.setBindTime(new Date());
automaticAssignDto.setUserId(Long.parseLong(paramMap.get("userId").toString()) );
int sum = automaticAssignMybatisDao.bindAutomaticAssignInfo(automaticAssignDto);
if(sum == 1){
return waitCheckList;
}else{
//已被更新 则再次获取
return automaticAssign(paramMap);
}
}else{
return null;
}
}
学习自 https://blog.csdn.net/zhangdehua678/article/details/79594212
案例二
package what21.thread.lock; public class OptimLockMain { // 文件版本号
static int version = 1;
// 操作文件
static String file = "d://IT小奋斗.txt"; /**
* 获取版本号
*
* @return
*/
public static int getVersion(){
return version;
} /**
* 更新版本号
*/
public static void updateVersion(){
version+=1;
} /**
* @param args
*/
public static void main(String[] args) {
for(int i=1;i<=5;i++){
new OptimThread(String.valueOf(i),getVersion(),file).start();
}
} } package what21.thread.lock; import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; public class OptimThread extends Thread { // 文件版本号
public int version;
// 文件
public String file; public OptimThread(String name,int version,String file){
this.setName(name);
this.version = version;
this.file = file;
} public void run() {
// 1. 读取文件
String text = read(file);
println("线程"+ getName() + ",文件版本号为:" + OptimLockMain.getVersion());
println("线程"+ getName() + ",版本号为:" + getVersion());
// 2. 写入文件
if(OptimLockMain.getVersion() == getVersion()){
println("线程" + getName() + ",版本号为:" + version + ",正在执行");
// 文件操作,这里用synchronized就相当于文件锁
// 如果是数据库,相当于表锁或者行锁
synchronized(OptimThread.class){
if(OptimLockMain.getVersion() == this.version){
// 写入操作
write(file, text);
// 更新文件版本号
OptimLockMain.updateVersion();
return ;
}
}
}
// 3. 版本号不正确的线程,需要重新读取,重新执行
println("线程"+ getName() + ",文件版本号为:" + OptimLockMain.getVersion());
println("线程"+ getName() + ",版本号为:" + getVersion());
System.err.println("线程"+ getName() + ",需要重新执行。");
} /**
* @return
*/
private int getVersion(){
return this.version;
} /**
* 写入数据
*
* @param file
* @param text
*/
public static void write(String file,String text){
try {
FileWriter fw = new FileWriter(file,false);
fw.write(text + "\r\n");
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 读取数据
*
* @param file
* @return
*/
public static String read(String file){
StringBuilder sb = new StringBuilder();
try {
File rFile = new File(file);
if(!rFile.exists()){
rFile.createNewFile();
}
FileReader fr = new FileReader(rFile);
BufferedReader br = new BufferedReader(fr);
String r = null;
while((r=br.readLine())!=null){
sb.append(r).append("\r\n");
}
br.close();
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
} /**
* @param content
*/
public static void println(String content){
System.out.println(content);
} }
学习自https://blog.csdn.net/qq897958555/article/details/79337064
Java乐观锁的实现原理(案例)的更多相关文章
- JAVA乐观锁实现-CAS
是什么 全称compare and swap,一个CPU原子指令,在硬件层面实现的机制,体现了乐观锁的思想. JVM用C语言封装了汇编调用.Java的基础库中有很多类就是基于JNI调用C接口实现了多线 ...
- Java乐观锁实现之CAS操作
介绍CAS操作前,我们先简单看一下乐观锁 与 悲观锁这两个常见的锁概念. 悲观锁: 从Java多线程角度,存在着“可见性.原子性.有序性”三个问题,悲观锁就是假设在实际情况中存在着多线程对同一共享的竞 ...
- 深入分析 Java 乐观锁
前言 激烈的锁竞争,会造成线程阻塞挂起,导致系统的上下文切换,增加系统的性能开销.那有没有不阻塞线程,且保证线程安全的机制呢?--乐观锁. 乐观锁是什么? 操作共享资源时,总是很乐观,认为自己可以成功 ...
- JAVA乐观锁、悲观锁实现
一.名词解释 1.悲观锁:认为每次对数据库的操作(查询.修改)都是不安全的,因此每次操作都会把这条数据锁掉,直到本次操作完毕释放该锁 2.乐观锁:查询数据的时候总是认为是安全的,不会锁数据:等到更新数 ...
- Java乐观锁、悲观锁
乐观锁 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号 ...
- java 乐观锁 vs 悲观锁
在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 悲观锁其实就是 完全同步 比如 sync ...
- java 乐观锁CAS
乐观锁是一种思想,本身代码里并没有lock或synchronized关键字进行修饰.而是采用一种version. 即先从数据库中查询一条记录得到version值,在更新这条记录时在where条件中对这 ...
- Elasticsearch系列---并发控制及乐观锁实现原理
概要 本篇主要介绍一下Elasticsearch的并发控制和乐观锁的实现原理,列举常见的电商场景,关系型数据库的并发控制.ES的并发控制实践. 并发场景 不论是关系型数据库的应用,还是使用Elasti ...
- Hibernate 悲观锁,乐观锁
业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无 ...
随机推荐
- datagrid 溢出文本显示省略号
.datagrid-cell, .datagrid-cell-group, .datagrid-header-rownumber, .datagrid-cell-rownumber{ -o-text- ...
- ADO.NET数据库编程
ADO.NET数据库编程 1.ADO.NET的相关概念. Microsoft的新一代技术,是ADO组件的后继者. 主要目的是在.NET Framework平台存取数据. 提供一致的对象模型,可以存取和 ...
- Layer Normalization
Ba, Jimmy Lei, Jamie Ryan Kiros, and Geoffrey E. Hinton. "Layer normalization." arXiv prep ...
- ECLIPSE 不能连接MYSQL 8 的现象
连接mysql 出现:java.sql.SQLException: Unable to load authentication plugin 'caching_sha2_password'. mysq ...
- Torch-RNN运行过程中的坑 [2](Lua的string sub函数,读取中文失败,乱码?)
0.踩坑背景 仍然是torch-rnn/LanguageModel.lua文件中的一些问题,仍然是这个狗血的LM:encode_string函数: function LM:encode_string( ...
- 安装ChemOffice 15.1就是这么简单
化学绘图软件ChemDraw出了最新版ChemOffice 15.1了,其下有三个不同组件,其中ChemDraw15.1 Pro使用范围最广.当我们下载完软件后就需要对其进行安装,一般按照流程来就没有 ...
- word页眉与页脚详解
1.如何隔离封面等不需要插入页码的页面: 首先插入分节符下一页(一定是分节符),再在下一页(即要开始插入页码的页面)选择视图-->页眉和页脚-->设置为取消链接到前一页.设置页码格式为起始 ...
- python技巧之下划线(二)
Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用’from module import *’导入 __xxx__ 系统定义名字 __xxx 类中的私有变量名 核心风格:避免用下划 ...
- 如何用redis来生成唯一Id
在之前的项目中需要用到一个自动增长的主键,该主键需要包含字母,所以没有办法用到数据库的自增主键.楼主要高手的指导下,发现Redis的RedisAtomicLong类可以解决这个麻烦.而且redis为单 ...
- Oracle中sql相关的命令
1.创建用户 SQL> -- 例如创建一个用户名为xiaoming,密码为a123的用户 SQL> create user xiaomingidentified by a123; 用户已创 ...