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

原文地址:https://www.cnblogs.com/baxianhua/p/9378031.html

时间: 2024-08-30 11:14:23

Java乐观锁的实现原理(案例)的相关文章

JAVA乐观锁实现-CAS

是什么 全称compare and swap,一个CPU原子指令,在硬件层面实现的机制,体现了乐观锁的思想. JVM用C语言封装了汇编调用.Java的基础库中有很多类就是基于JNI调用C接口实现了多线程同步更新的功能. 原理 CMS有三个操作数:当前主内存变量的值V,线程本地变量预期值A,线程本地待更新值B.当需要更新变量值的时候,会先获取到内存变量值V然后很预期值A进行比较,如果相同则更新为B,如果不同,则将最新的变量值更新到预期值中再重新尝试上面的步骤,直到成功为止. 举例 以基于CAS实现

JAVA乐观锁、悲观锁实现

一.名词解释 1.悲观锁:认为每次对数据库的操作(查询.修改)都是不安全的,因此每次操作都会把这条数据锁掉,直到本次操作完毕释放该锁 2.乐观锁:查询数据的时候总是认为是安全的,不会锁数据:等到更新数据的时候会判断这个数据是否被人修改过,如果有人修改过了则本次修改失败 二.使用过程 1.悲观锁:悲观锁的内部实现是采用的数据库内部的锁机制,一个典型的依赖数据库的悲观锁调用: SELECT * FROM TABLE WHERE ID='1' FOR UPDATE; 这条语句锁定了TABLE表总id=

java 乐观锁与悲观锁

悲观锁,就是不管是否发生多线程冲突,只要存在这种可能,就每次访问都加锁. 乐观锁,就是通过标记值控制,每次操作前通过标记值判断是否是最新内容,最新内容就可以操作,不是最新的就继续循环判断标记值,直到是最新类容. 在大量冲突发生时,悲观锁的锁消耗大,乐观锁的读取次数会多. 版权声明:本文为博主原创文章,未经博主允许不得转载.

java乐观锁使用

乐观锁,大多是基于数据版本   Version )记录机制实现.何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通 过为数据库表增加一个 "version" 字段来 实现. 读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一.此时,将提 交数据的版本数据与数据 库表对应记录的当前版本信息进行比对,如果提交的数据 版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据.对于上面修改用户帐户信息 的例子而言,假设数据库中帐户信息表中有一个 ve

Java Synchronized 锁的实现原理详解及偏向锁-轻量锁-重量锁

Synchronize是重量级锁吗?是互斥锁吗? 它的实现原理? 前言 线程安全是并发编程中的重要关注点,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多个线程共同操作共享数据.因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只

Java Synchronized 锁的实现原理与应用 (偏向锁,轻量锁,重量锁)

简介 在Java SE 1.6之前,Synchronized被称为重量级锁.在SE 1.6之后进行了各种优化,就出现了偏向锁,轻量锁,目的是为了减少获得锁和释放锁带来的性能消耗. Synchroized的使用(三种形式)(1) 对于普通同步方法,锁是当前实例对象.如下代码示例:解释:对于set和get方法来说,都是在方法上使用了同步关键字,所以他们是同步方法,锁的就是当前的实例对象,怎么理解了,看下面的main方法,就是这个new的实例对象.所以他们的锁对象都是synchronizedMetho

Hibernate 悲观锁,乐观锁

业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的"锁",即给我们选定的目标数据上锁,使其无法被其它程序修改. Hibernate 支持两种锁机制: 1. 悲观锁(Pessimistic Locking) 从加载对象就开始锁定.修改过程中一直是锁.直到事务commit()提交后再解锁. session.load(Info.class,"p003",LockOp

mysql的乐观锁与悲观锁

乐观锁 总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现. 例如: 有这样一个表: 每次更新时update在条件后再附加一个时间为条件: update user_info set password='somelog' where username='somelog' and time='2018-07-11'; 因为如果并发操作,同一刻查询的时间一样,但是先插

HIbernate乐观锁与悲观锁

悲观锁 从加载对象就开始锁定.修改过程中一直是锁.直到commit()提交后再解锁.只需要在加载对象时加上(LockOptions.UPGRADE)即可,如下所示 Info info=session.load(Info.class,"p003",LockOptions.UPGRADE); 这样带来的问题就是,当访问量大的时候就会造成排队等待的后果. 乐观锁 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制.悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性.但随之而