亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java基于redis實現(xiàn)分布式鎖代碼實例

 更新時間:2020年04月09日 10:43:38   作者:繁華亂世  
這篇文章主要介紹了Java基于redis實現(xiàn)分布式鎖代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

為什么會有這個需求:

例如一個簡單用戶的操作,一個線程去修改用戶狀態(tài),首先在在內(nèi)存中讀出用戶的狀態(tài),然后在內(nèi)存中進行修改,然后在存到數(shù)據(jù)庫中。在單線程中,這是沒有問題的。但是在多線程中由于讀取,修改,寫入是三個操作,不是原子操作(同時成功或失敗),因此在多線程中會存在數(shù)據(jù)的安全性問題。

這個問題的話,就可以用分布式鎖在限制程序的并發(fā)執(zhí)行。

實現(xiàn)思路:

就是進來一個先占位,當(dāng)別的線程進來操作的時候,發(fā)現(xiàn)有人占位了,就會放棄或者稍后再試。

占位的實現(xiàn):

在redis中的setnx命令來實現(xiàn),redis命令可以參考我這篇博客https://www.cnblogs.com/javazl/p/12657280.html,默認(rèn)set命令就是存值,當(dāng)key存在的時候,set就會覆蓋key的value值,而setnx則不會。當(dāng)沒有key的時候,setnx就會進來先占位,當(dāng)key存在了,其他的setnx就進不來了。。等到第一個執(zhí)行完成后,在del命令釋放位子。

代碼實現(xiàn):

public class LockTest {
  public static void main(String[] args) {
    Redis redis = new Redis();
    redis.execute(jedis->{
      Long setnx = jedis.setnx("k1", "v1");
     //setnx的返回值為long類型
      if (setnx == 1) {
        //沒人占位
        jedis.set("name", "zl");
        String name = jedis.get("name");
        System.out.println(name);
        //釋放資源
         jedis.del("k1");
      }else{
        //有人占位,停止/暫緩 操作
      }
    });
  }
}

上邊代碼中,就是一個簡易的分布式鎖的實現(xiàn),但是有一個問題。就是如果在占位后釋放前掛了。那么這個線程會一直釋放不了,也就是del命令沒有調(diào)用,后面的全部請求都阻塞到這里,鎖就變成了死鎖。因此這里需要去優(yōu)化。

優(yōu)化的方法就是加過期時間,確保鎖在一定時間后能夠釋放.

public class LockTest {
  public static void main(String[] args) {
    Redis redis = new Redis();
    redis.execute(jedis->{
      Long setnx = jedis.setnx("k1", "v1");
      if (setnx == 1) {
        //給鎖添加一個過期時間,防止應(yīng)用在運行過程中拋出異常導(dǎo)致鎖無法及時得到釋放
        jedis.expire("k1", 5);
        //沒人占位
        jedis.set("name", "zl");
        String name = jedis.get("name");
        System.out.println(name);
        jedis.del("k1");
      }else{
        //有人占位,停止/暫緩 操作
      }
    });
  }

這樣處理后,就可以保證鎖可以正常的釋放。但是會有一個新的問題,就是如果在取鎖和設(shè)置過期時間服務(wù)器掛掉了,因為取鎖,也就是setnx和設(shè)置過期時間是兩個操作,不具備原子性所以不可能同時完成。這個鎖就會被一直占用,無法得到釋放,成為死鎖。那么如何解決呢?

在redis2.8之后,setnx和expireke可以通過一個命令一起執(zhí)行,讓兩個操作變成一個,就會解決這個問題。

優(yōu)化實現(xiàn):

public class LockTest {
  public static void main(String[] args) {
    Redis redis = new Redis();
    redis.execute(jedis->{
     //將兩個操作合并成一個,nx就是setnx,ex就是expire
      String set = jedis.set("k1", "v1", new SetParams().nx().ex(5));
     //操作結(jié)果為okhuo或者error
      if (set !=null && "OK".equals(set)) {
     //給鎖添加一個過期時間,防止應(yīng)用在運行過程中拋出異常導(dǎo)致鎖無法及時得到釋放
        jedis.expire("k1", 5);
        //沒人占位
        jedis.set("name", "zl);
        String name = jedis.get("name");
        System.out.println(name);
      //釋放資源
        jedis.del("k1");
      }else{
        //有人占位,停止/暫緩 操作
      }
    });
  }
}

用過期時間優(yōu)化后,雖然解決了死鎖的問題,但是又有一個新的問題產(chǎn)生,就是超時問題:

舉個例子:如果要執(zhí)行的業(yè)務(wù)很耗時,可能會出現(xiàn)紊亂,當(dāng)?shù)匾粋€線程獲取到鎖的時候,開始執(zhí)行業(yè)務(wù)代碼,但是業(yè)務(wù)代碼很耗時,假如過期時間是3秒,而業(yè)務(wù)執(zhí)行需要5秒,這樣,鎖就會提前釋放,然后第二個線程獲取到鎖并開始執(zhí)行。當(dāng)執(zhí)行到第2秒的時候,第一個鎖也執(zhí)行完了,此時第一個線程會釋放第二個線程的鎖,然后第三個線程繼續(xù)獲取鎖并執(zhí)行,當(dāng)?shù)降?秒的時候第二個線程執(zhí)行完了,那么又會提前釋放鎖,一直如此循環(huán),會造成線程的紊亂。

那么解決的思路主要有兩種

盡量避免耗時操作。

去處理鎖,給鎖的value設(shè)置隨機數(shù)或隨機字符串,每當(dāng)要釋放的時候去判斷這個value的值,如果是的話就去釋放,如果不是就不釋放,舉個例子,假設(shè)第一個線程進來,它獲取鎖的value是1,如果發(fā)生超時就會進入下一個線程,下一個線程會獲取新的value為

3,在釋放第二個所之前先去獲取value并比較,發(fā)現(xiàn)1不等于三,那么就不去釋放鎖。
第一種的話沒啥說的,但是第二種的話會有一個問題,就是釋放鎖會查看value,然后比較,然后釋放,會有三個操作,那么就不具備原子性,這樣操作的話,會出現(xiàn)死鎖。這里我們可以使用Lua腳本去處理。

Lua腳本的特點:

1.使用方便,redis內(nèi)置了對Lua腳本的支持。

2.Lua可以在redis服務(wù)端原子性的執(zhí)行多個redis命令

3.由于網(wǎng)絡(luò)的原因會影響到redis的性能,因此,使用Lua可以讓多個命令同時執(zhí)行,降低了網(wǎng)絡(luò)給redis帶來的性能問題。

在redis中如何使用Lua腳本:

1.在redis服務(wù)端寫好,然后在java業(yè)務(wù)中調(diào)用腳本

2.可以直接在java中直接去寫,寫好后,需要執(zhí)行時,每次將腳本發(fā)送到redis中去執(zhí)行。

創(chuàng)建Lua腳本:

//用redis.call調(diào)用一個redis命令,調(diào)的是get命令,這個key是從外面?zhèn)鬟M來的keyif redis.call("get",KEYS[1])==ARGV[1] then//如果相等就去操作釋放命令
  return redis.call("del",KEYS[1])
else
 return 0
end

可以給Lua腳本求一個SHA1和:

cat lua/equal.lua | redis-cli -a root script load --pipe

script load這個命令會在Redis中緩存Lua腳本,并返回腳本內(nèi)容的SHA1校驗和,然后在java中調(diào)用時,傳入SHA1校驗和作為參數(shù),這樣redis服務(wù)端就知道執(zhí)行那個腳本了。

接下來在java中編寫

public static void main(String[] args) {
    Redis redis = new Redis();
    for (int i = 0; i < 2; i++) {
      redis.execute(jedis -> {
        //1.先獲取一個隨機字符串
        String value = UUID.randomUUID().toString();
        //2.獲取鎖
        String k1 = jedis.set("k1", value, new SetParams().nx().ex(5));
        //3.判斷是否成功拿到鎖
        if (k1 != null && "OK".equals(k1)) {
          //4. 具體的業(yè)務(wù)操作
          jedis.set("site", "zl");
          String site = jedis.get("site");
          System.out.println(site);
          //5.釋放鎖
          jedis.evalsha("b8059ba43af6ffe8bed3db65bac35d452f8115d8", 
Arrays.asList("k1"), Arrays.asList(value));
        } else {
          System.out.println("沒拿到鎖");
        }
      });
    }
  }
}

這樣處理的話,就解決了死鎖的問題。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • XSS攻擊以及java應(yīng)對xss攻擊的解決方案

    XSS攻擊以及java應(yīng)對xss攻擊的解決方案

    XSS是跨站腳本攻擊Cross Site Scripting的縮寫,為了和層疊樣式表CSS加以區(qū)分,因此將跨站腳本攻擊縮寫為XSS,這篇文章主要給大家介紹了關(guān)于XSS攻擊以及java應(yīng)對xss攻擊的解決方案,需要的朋友可以參考下
    2024-02-02
  • java圖搜索算法之DFS與BFS詳解

    java圖搜索算法之DFS與BFS詳解

    這篇文章主要為大家介紹了java數(shù)據(jù)結(jié)構(gòu)中可以秒殺一切圖算法的DFS與BFS作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11
  • 詳解 問題:HttpServlet cannot be resolved to a type

    詳解 問題:HttpServlet cannot be resolved to a type

    這篇文章主要介紹了詳解 問題:HttpServlet cannot be resolved to a type的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • 一文搞懂Mybatis中Mapper配置文件獲取參數(shù)的五種方式

    一文搞懂Mybatis中Mapper配置文件獲取參數(shù)的五種方式

    這篇文章主要介紹了Mybatis中Mapper配置文件獲取參數(shù)的五種方式,文中通過代碼示例講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-03-03
  • mybatis如何返回某列的最大值

    mybatis如何返回某列的最大值

    這篇文章主要介紹了mybatis如何返回某列的最大值操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring核心容器之Bean創(chuàng)建過程詳解

    Spring核心容器之Bean創(chuàng)建過程詳解

    這篇文章主要介紹了Spring核心容器之Bean創(chuàng)建過程詳解,獲取?Bean?的方法是?getBean,其來自?BeanFactory?繼承的AbstractAutowireCapableBeanFactory?抽象類繼承的AbstractBeanFactory?抽象類中,需要的朋友可以參考下
    2023-11-11
  • Jenkins?Pipline實現(xiàn)及原理示例解析

    Jenkins?Pipline實現(xiàn)及原理示例解析

    這篇文章主要為大家介紹了Jenkins?Pipline實現(xiàn)及原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • Java編程BigDecimal用法實例分享

    Java編程BigDecimal用法實例分享

    這篇文章主要介紹了Java編程BigDecimal用法實例分享,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • java開發(fā)模式的深度研究

    java開發(fā)模式的深度研究

    下面小編就為大家?guī)硪黄钊肜斫鈐ava工廠模式。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-07-07
  • java實現(xiàn)單鏈表之逆序

    java實現(xiàn)單鏈表之逆序

    這篇文章主要介紹了應(yīng)用java語言實現(xiàn)單鏈表逆序,,需要的朋友可以參考下
    2015-07-07

最新評論