1. 程式人生 > >Java樂觀鎖的實現原理(案例)

Java樂觀鎖的實現原理(案例)

extends 默認 tomat 讀取數據 pac creat fifo for ava

簡要說明:

表設計時,需要往表裏加一個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樂觀鎖的實現原理(案例)