并发编程-锁的发展和主流分布式锁比较总结

1、锁的发展html

       系统结构由传统的“单应用服务--》SOA --》微服务 --》无服务器” 的演进过程当中,场景愈来愈复杂,由单体应用的但进程中多线程并发的内存锁,随着互联网场景愈来愈复杂,在复杂的系统交互过程当中存在大量的并发。分布式并发锁概念就营运而生web

2、锁的介绍redis

    一、进程下单线程模式,更改成多钱成并发模式,因而就产生了线程锁,也就是常说的内存锁,是基于单线程中多线程的锁控制,基于这种方式又演变出来文件锁等,可是都是基于控制多线程并发的锁机制sql

    二、分布式以及微服务的兴起,程序逐步从单进程演变为多进程。进程之间就会产生须要处理并发业务,须要实现业务锁,因此就产生了分布式锁的解决方案。数据库

 目前市面上解决分布式锁的方案主要有如下几种:api

(1)基于数据库表作乐观锁(乐观锁和悲观锁定义请参照-锁实例介绍找那个单进程内并发锁的博客),用于分布式锁。缓存

(2)使用memcached的add()方法,用于分布式锁。服务器

(3)基于redisson实现分布式锁(redis官方推荐)多线程

不经常使用可是能够用于技术方案探讨的:并发

(1)使用memcached的cas()方法,用于分布式锁。 

(2)使用redis的setnx()、get()、getset()方法,使用redis的setnx()、expire()方法,用于分布式锁,使用watch、multi、exec命令,用于分布式锁。这几种方法都能根据redis的特性实现分布式锁,可是在集群状况下,不少极端场景会出现问题

(3)使用zookeeper,用于分布式锁。

3、锁实例介绍

一、单进程多线程并发锁。

     请参看博客:http://www.cnblogs.com/dennyzhangdd/p/6925473.html

                         http://www.cnblogs.com/zhimingyang/p/5702752.html(ReenTrantLock源码解析

    注意: 因为1.5将Synchronized优化后,使得性能大大提高,那么何时使用什么状况下使用ReenTrantLock:

      答案是,若是你须要实现ReenTrantLock的三个独有功能时。

       ReenTrantLock独有的能力:

(1) ReenTrantLock能够指定是公平锁仍是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先得到锁。

(2)ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒须要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒所有线程。

(3) ReenTrantLock提供了一种可以中断等待锁的线程的机制,经过lock.lockInterruptibly()来实现这个机制。

二、分布式并发锁

(1)数据库实现乐观锁(新手推荐)

       乐观锁上文说了是一种思想,相对悲观锁而言,乐观锁假设认为数据通常状况下不会产生并发冲突,因此在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,若是发现并发冲突了,则让返回用户错误的信息,让用户决定如何去作。

      具体实现描述:

  • 增长数据版本控制标识:数据库表中增长一个version属性,属性由1递增
  • 数据更新:每条数据在操做以后会增长一个version版本,好比每次操做增长1
  • 数据校验:操做每条数据以前,先查询数据库中记录的版本号,并与传入的操做版本号比对是否一致。若是发生冲突,则返回让开发者本身决定如何操做

(2)memcached实现分布式锁(没有实现过,不作过多评价)

  • 实现原理:

        memcached带有add函数,利用add函数的特性便可实现分布式锁。add和set的区别在于:若是多线程并发set,则每一个set都会成功,但最后存储的值以最后的set的线程为准。而add的话则相反,add会添加第一个到达的值,并返回true,后续的添加则都会返回false。利用该点便可很轻松地实现分布式锁。

  • 优势

        并发高效。

  • 缺点

          --》memcached采用列入LRU置换策略,因此若是内存不够,可能致使缓存中的锁信息丢失。

          --》memcached没法持久化,一旦重启,将致使信息丢失。

(3)redis实现分布式锁方法(老死机推荐)

         在讲解redis锁时,这里只讲解在集群状况下的分布式锁实现(单点redis和集群redis的实现原理是不同的)

        以下是一篇分布式锁的官方翻译微博:http://ifeve.com/redis-lock/,目前redis官方推荐使用的Redisson就提供了分布式锁和相关服务。 

  • maven引入redisson组件包
    <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
             <version>2.7.0</version> 
     </dependency>
  • 使用redisson,最好采用redis 2.6.0以上版本,由于redosson一些后台命令采用eval的命令 
    import org.redisson.Redisson;
    import org.redisson.api.RAtomicLong;
    import org.redisson.config.Config;
    
    public class RedissonManager {
    
        private static final String RAtomicName = "genId_";
    
        private static Config config = new Config();
        private static Redisson redisson = null;
    
       public static void init(String key,String value){
            try {
    /*            config.useClusterServers() //这是用的集群server
                        .setScanInterval(2000) //设置集群状态扫描时间
                        .setMasterConnectionPoolSize(10000) //设置链接数
                        .setSlaveConnectionPoolSize(10000)
                        .addNodeAddress("127.0.0.1:6379");*/
            	if(key==null || "".equals(key)){
            		key=RAtomicName;
            	}
            	config.useSingleServer().setAddress("127.0.0.1:6379");
                redisson = (Redisson) Redisson.create(config);
                //清空自增的ID数字
                RAtomicLong atomicLong = redisson.getAtomicLong(key);
                long pValue=1;
                if(value!=null && !"".equals(value)){
                	pValue = Long.parseLong(value);
                }
                atomicLong.set(pValue);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        public static Redisson getRedisson(){
            return redisson;
        }
    
        /** 获取redis中的原子ID */
        public static Long nextID(){
            RAtomicLong atomicLong = getRedisson().getAtomicLong(RAtomicName);
           //原子性的获取下一个ID,递增1 
           atomicLong.incrementAndGet();
            return atomicLong.get();
        }
    }
  • 加锁和释放锁的方法,设置超时 
    public class DistributedRedisLock {
        private static Redisson redisson = RedissonManager.getRedisson();
        private static final String LOCK_TITLE = "redisLock_";
    
        public static boolean acquire(String lockName){
            String key = LOCK_TITLE + lockName;
            RLock mylock = redisson.getLock(key);
            mylock.lock(2, TimeUnit.MINUTES); //lock提供带timeout参数,timeout结束强制解锁,防止死锁
            System.err.println("======lock======"+Thread.currentThread().getName());
            return  true;
        }
    
        public static void release(String lockName){
            String key = LOCK_TITLE + lockName;
            RLock mylock = redisson.getLock(key);
            mylock.unlock();
            System.err.println("======unlock======"+Thread.currentThread().getName());
        }
    }
  • 在web端,controller中 
    @RequestMapping("/redder")
        @ResponseBody
        public String redder() throws IOException{
        	 String key = "test123";
        	 
          DistributedRedisLock.acquire(key);
        		
        		 
          Long result =  RedissonManager.nextID();  
    
        	DistributedRedisLock.release(key);
        	return ""+result; 
        }

 

4、总结

       目前对于分布式锁的场景很是多,可是如今目前主要的这几种中redis分布式锁的方式目前市场上用的最多。同时笔者也更推荐redisson的方式。

        比较总结

        一、单JVM锁

        (1)synchronized同步锁(基于JVM源生synchronized关键字实现,新手推荐)

                适用于低并发的状况,性能稳定。

       (2)ReentrantLock可重入锁(基于JDK实现,需显示获取锁,释放锁,须要指定公平、非公平或condition时使用。)

                适用于低、高并发的状况,性能较高

       (3)ReentrantReadWriteLock可重入读写锁(基于JDK实现,需显示获取锁,释放锁。老司机推荐)

                适用于读多写少的状况。性能高。

        (4)StampedLock戳锁(基于JDK实现,需显示获取锁,释放锁。老司机推荐)

                 JDK8才有,适用于高并发且读远大于写时,支持乐观读,票据校验失败后可升级悲观读锁,性能极高!

            二、分布式锁

             (1)悲观锁:select for update(基于数据库锁实现,不推荐

                    sql直接使用,但水很深。涉及数据库ACID原理+隔离级别+不一样数据库规范

              (2)乐观锁:版本控制(基于数据库锁实现,新手推荐

                    本身实现字段版本控制

              (3)redisson(基于redis缓存实现,老司机推荐)

                    性能极高,支持除了分布式锁外还实现了分布式对象、分布式集合等极端强大的功能

              (4)zookeeper(基于zookeeper实现,老司机推荐)

                    性能较高,除支持分布式锁外,还实现了master选举、节点监听()、分布式队列、Barrier、AtomicLong等计数器

              (5)memcached(基于memcached实现,老司机推荐)

                    有明显的缺点,重启会丢失锁,性能很高,可是有弊端,根据不一样的场景选择使用

相关文章
相关标签/搜索