併發編程-鎖的發展和主流分佈式鎖比較總結

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實現,老司機推薦)

                    有明顯的缺點,重啓會丟失鎖,性能很高,可是有弊端,根據不一樣的場景選擇使用

相關文章
相關標籤/搜索