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

一、锁的发展

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

二、锁的介绍

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

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

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

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

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

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

不常用但是可以用于技术方案探讨的:

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

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

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

三、锁实例介绍

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

     请参看博客: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()来实现这个机制。

2、分布式并发锁

(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; 
        }

 

四、总结

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

        比较总结

        1、单JVM锁

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

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

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

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

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

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

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

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

            2、分布式锁

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

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

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

                    自己实现字段版本控制

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

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

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

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

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

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

 

 

转载于:https://my.oschina.net/jiahaov/blog/1555232

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值