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 悲观锁,乐观锁
业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无 ...
随机推荐
- JavaWeb——监听器
监听器简介 监听器是指专门用于在其他对象身上发生的事件或者状态改变进行监听和相应处理的对象,当被监听的对象发生变化时立即采取相应的行动. 例如我们要实现统计一个网站的在线人数,就可以在Web应用应用程 ...
- 【vijos】1769 网络的关键边(割边)
https://vijos.org/p/1769 啊,割边写挫了害得我交了那么多发... 本题多想想就出来了.. 首先求出割边,显然关键边就在割边上. 求完割边后,我们先从一个点dfs,维护A的点数和 ...
- 拦截asp.net输出流并进行处理的方法
本文实例主要实现对已经生成了HTML的页面做一些输出到客户端之前的处理. 方法的实现原理是:把Response的输出重定向到自定义的容器内,也就是我们的StringBuilder对象里,在HTML所有 ...
- 用MathType编辑上下尖括号有什么技巧
在MathType中,同一个数学符号可以进行各种变换方向的使用,就比如箭头符号,任意方向都可以使用,这也是很常见的.数学中的符号能够根据各种特殊需要进行灵活使用,除了箭头符号之外,其它符号也可以,比如 ...
- Response响应对象
1.HttpServletResponse HttpServletResponse是一个定义在Servlet API中的接口,继承自ServletReponse接口,用于封装HTTP响应消息.HTTP ...
- 记一次androidd登陆页面的实现
背景 先说个事:最近我准备做个开源的博客园android客户端!符合Google最新的material design设计风格的!不知道有没有小伙伴愿意和我一起做呢?如果有愿意的,请私信我哦!!!!我还 ...
- jupyter 修改工作路径
在所需打开的目录中新建一个runJupyter.bat文件 将内容修改为: cd ......jupyter notebook 注1:上述两行中,第一行的......为路径(可以不添加,可空着不填), ...
- nginx默认访问目录时显示403错误
author :headsen chen date: 2018-04-25 17:17:25 nginx安装完成后默认的http页面里访问目录的话会报403的错误: 和Apache进行比较,Apac ...
- CSS3的自定义字体@font-face:将图片ICON转为字体
大家都知道现在各个浏览器都支持CSS3的自定义字体(@font-face),包括IE6都支持,只是各自对字体文件格式的支持不太一样.那么对于网站中用到的各种icon,我们就可以尝试使用font来实现, ...
- 【BZOJ4345】[POI2016]Korale 堆(模拟搜索)
[BZOJ4345][POI2016]Korale Description 有n个带标号的珠子,第i个珠子的价值为a[i].现在你可以选择若干个珠子组成项链(也可以一个都不选),项链的价值为所有珠子的 ...