zoukankan      html  css  js  c++  java
  • 乐观锁与悲观锁

    Java乐观锁的实现原理(案例)

     

    简要说明:

    表设计时,需要往表里加一个version字段。每次查询时,查出带有version的数据记录更新数据时,判断数据库里对应id的记录的version是否和查出的version相同。若相同,则更新数据并把版本号+1;若不同,则说明,该数据发送并发,被别的线程使用了,进行递归操作,再次执行递归方法,知道成功更新数据为止

    简单说说乐观锁。乐观锁是相对于悲观锁而言。悲观锁认为,这个线程,发生并发的可能性极大,线程冲突几率大,比较悲观。一般用synchronized实现,保证每次操作数据不会冲突。乐观锁认为,线程冲突可能性小,比较乐观,直接去操作数据,如果发现数据已经被更改(通过版本号控制),则不更新数据,再次去重复 所需操作,知道没有冲突(使用递归算法)。

        因为乐观锁使用递归+版本号控制  实现,所以,如果线程冲突几率大,使用乐观锁会重复很多次操作(包括查询数据库),尤其是递归部分逻辑复杂,耗时和耗性能,是低效不合适的,应考虑使用悲观锁。

       乐观锁悲观锁的选择:

            乐观锁:并发冲突几率小,对应模块递归操作简单    时使用

            悲观锁:并发几率大,对应模块操作复杂 时使用

    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 + "
    ");
                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("
    ");
                }
                br.close();
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return sb.toString();
        }
        
        /**
         * @param content
         */
        public static void println(String content){
            System.out.println(content);
        }
        
    }

    1、这里线程对文件写入的操作,就相当于修改表中的一条数据。

    2、多个线程需要对这个文件写入时,同时只能有一个线程写入该文件。

    3、同时只能有一个线程写入文件,就相当于与表的行锁,同一时间只允许一个线程操作。

    4、线程初始化时,持有相同的版本号,写入文件操作完成后,然后变更版本号。

    5、其他线程再次操作时,比较版本号,版本号较低的不在进行写入操作,或者抛出异常,或者再次执行该操作,这样就防止产生脏数据了。

     
  • 相关阅读:
    django实现github第三方本地登录
    django扩展User模型(model),profile
    WSGI
    Linux查看配置文件中未被注释的有效配置行
    virsh 命令
    qemu-img命令
    python logging
    oslo.messaging
    集成kafka的身份认证(SASL/PLAIN)到kubernets环境中
    Helm基础知识
  • 原文地址:https://www.cnblogs.com/dzh1990/p/13801091.html
Copyright © 2011-2022 走看看