簡要說明:
表設計時,需要往表里加一個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
