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 悲观锁,乐观锁
业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无 ...
随机推荐
- 什么是 end-to-end 神经网络?——知乎解答
什么是 end-to-end 神经网络? https://www.zhihu.com/question/51435499 解答1 张旭 像机器一样学习,像人一样生活 YJango 等 端到端指的是 ...
- oracle11g卸载,10g之类版本通用
鉴于oracle一些比较稀奇的问题,本人没碰到的不能帮忙解决的.而且比较着急赶时间的亲们,我就只能推荐先卸载在安装的办法了,这个方法一般用时也就1个小时到1个半小时之间,切记按步骤删除,别漏删了,不然 ...
- 详略。。设计模式2——单例变形(多例).。。。studying
★ 缓存在单例中的使用("单例+缓存"技术) 缓存在编程中使用非常频繁,有着非常关键的数据,它可以帮助程序实现以空间换取时间, 通常被设计成整个应用程序所共享的一个空间,现要求实现 ...
- 用ChemDraw画3D图的方法
在绘制化学图形的时候,很多的用户都会发现很多的图形都是三维的,这个时候就需要找一款能够绘制3D图形的化学绘图软件.ChemOffice 15.1是最新的化学绘图工具套件,总共有三个组件,其中ChemD ...
- (1)、hive框架搭建和架构简介
一.简介 Hive是基于hadoop的一个数据仓库工具,有助于查询和管理分布式存储系统中的数据集,非常适合数据仓库的统计分析 Hive 不适合用于连机事物处理.也不提供实时查询,比较适合在大量不可变数 ...
- JavaScript------获取表单信息
<form name="fname"> <input type="text" name="user" /> < ...
- spring boot 整合kafka 报错 Exception thrown when sending a message with key='null' and payload=JSON to topic proccess_trading_end: TimeoutException: Failed to update metadata after 60000 ms.
org.springframework.kafka.support.LoggingProducerListener- Exception thrown when sending a message w ...
- 图片和byte[]数组互转
一.图片转成byte[]数组. import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io ...
- iOS 计算时间差
/** * 计算指定时间与当前的时间差 * @param compareDate 某一指定时间 * @return 多少(秒or分or天or月or年)+前 (比如,3天前.10分钟前) */ +(NS ...
- Kubernetes之kubectl常用命令
最近项目有用到Kubernetes作集群配置,所以学习下相关命令,记录下以备下次使用... kubectl help 显示具体的用法 kubectl controls the Kubernetes c ...