最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redisson分布式鎖源碼解析

 更新時間:2018年08月02日 08:25:22   投稿:laozhang  
文章給大家分享了關(guān)于Redisson分布式鎖源碼相關(guān)的知識點內(nèi)容,有興趣的朋友們可以參考學(xué)習(xí)下。

Redisson鎖繼承Implements Reentrant Lock,所以具備 Reentrant Lock 鎖中的一些特性:超時,重試,可中斷等。加上Redisson中Redis具備分布式的特性,所以非常適合用來做Java中的分布式鎖。 下面我們對其加鎖、解鎖過程中的源碼細節(jié)進行一一分析。

鎖的接口定義了一下方法:

分布式鎖當中加鎖,我們常用的加鎖接口:

boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;

下面我們來看一下方法的具體實現(xiàn):

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
  long time = unit.toMillis(waitTime);
  long current = System.currentTimeMillis();
  final long threadId = Thread.currentThread().getId();
  Long ttl = tryAcquire(leaseTime, unit, threadId);
  // lock acquired
  if (ttl == null) {
   return true;
  }
  
  time -= (System.currentTimeMillis() - current);
  if (time <= 0) {
   acquireFailed(threadId);
   return false;
  }
  
  current = System.currentTimeMillis();
  final RFuture subscribeFuture = subscribe(threadId);
  if (!await(subscribeFuture, time, TimeUnit.MILLISECONDS)) {
   if (!subscribeFuture.cancel(false)) {
    subscribeFuture.addListener(new FutureListener() {
     @Override
     public void operationComplete(Future future) throws Exception {
      if (subscribeFuture.isSuccess()) {
       unsubscribe(subscribeFuture, threadId);
      }
     }
    });
   }
   acquireFailed(threadId);
   return false;
  }

  try {
   time -= (System.currentTimeMillis() - current);
   if (time <= 0) {
    acquireFailed(threadId);
    return false;
   }
  
   while (true) {
    long currentTime = System.currentTimeMillis();
    ttl = tryAcquire(leaseTime, unit, threadId);
    // lock acquired
    if (ttl == null) {
     return true;
    }

    time -= (System.currentTimeMillis() - currentTime);
    if (time = 0 && ttl < time) {
     getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
    } else {
     getEntry(threadId).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
    }

    time -= (System.currentTimeMillis() - currentTime);
    if (time <= 0) {
     acquireFailed(threadId);
     return false;
    }
   }
  } finally {
   unsubscribe(subscribeFuture, threadId);
  }
//  return get(tryLockAsync(waitTime, leaseTime, unit));
 }

首先我們看到調(diào)用tryAcquire嘗試獲取鎖,在這里是否能獲取到鎖,是根據(jù)鎖名稱的過期時間TTL來判定的(TTL

下面我們接著看一下tryAcquire的實現(xiàn):

private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) {
 return get(tryAcquireAsync(leaseTime, unit, threadId));
}

可以看到真正獲取鎖的操作經(jīng)過一層get操作里面執(zhí)行的,這里為何要這么操作,本人也不是太理解,如有理解錯誤,歡迎指正。

get 是由CommandAsyncExecutor(一個線程Executor)封裝的一個Executor

設(shè)置一個單線程的同步控制器CountDownLatch,用于控制單個線程的中斷信息。個人理解經(jīng)過中間的這么一步:主要是為了支持線程可中斷操作。

public V get(RFuture future) {
 if (!future.isDone()) {
  final CountDownLatch l = new CountDownLatch(1);
  future.addListener(new FutureListener() {
   @Override
   public void operationComplete(Future future) throws Exception {
    l.countDown();
   }
  });
  
  boolean interrupted = false;
  while (!future.isDone()) {
   try {
    l.await();
   } catch (InterruptedException e) {
    interrupted = true;
   }
  }
  
  if (interrupted) {
   Thread.currentThread().interrupt();
  }
 }

 // commented out due to blocking issues up to 200 ms per minute for each thread:由于每個線程的阻塞問題,每分鐘高達200毫秒
 // future.awaitUninterruptibly();
 if (future.isSuccess()) {
  return future.getNow();
 }

 throw convertException(future);
}

我們進一步往下看:

private RFuture tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId) {
 if (leaseTime != -1) {
  return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
 }
 RFuture ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
 ttlRemainingFuture.addListener(new FutureListener() {
  @Override
  public void operationComplete(Future future) throws Exception {
   if (!future.isSuccess()) {
    return;
   }

   Long ttlRemaining = future.getNow();
   // lock acquired
   if (ttlRemaining == null) {
    scheduleExpirationRenewal(threadId);
   }
  }
 });
 return ttlRemainingFuture;
}

首先判斷鎖是否有超時時間,有過期時間的話,會在后面獲取鎖的時候設(shè)置進去。沒有過期時間的話,則會用默認的

private long lockWatchdogTimeout = 30 * 1000;

下面我們在進一步往下分析真正獲取鎖的操作:

RFuture tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand command) {
 internalLockLeaseTime = unit.toMillis(leaseTime);

 return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
    "if (redis.call('exists', KEYS[1]) == 0) then " +
     "redis.call('hset', KEYS[1], ARGV[2], 1); " +
     "redis.call('pexpire', KEYS[1], ARGV[1]); " +
     "return nil; " +
    "end; " +
    "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
     "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
     "redis.call('pexpire', KEYS[1], ARGV[1]); " +
     "return nil; " +
    "end; " +
    "return redis.call('pttl', KEYS[1]);",
    Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}

我把里面的重點信息做了以下三點總結(jié):

1:真正執(zhí)行的是一段具有原子性的Lua腳本,并且最終也是由CommandAsynExecutor去執(zhí)行。

2:鎖真正持久化到Redis時,用的hash類型key field value

3:獲取鎖的三個參數(shù):getName()是邏輯鎖名稱,例如:分布式鎖要鎖住的methodName+params;internalLockLeaseTime是毫秒單位的鎖過期時間;getLockName則是鎖對應(yīng)的線程級別的名稱,因為支持相同線程可重入,不同線程不可重入,所以這里的鎖的生成方式是:UUID+":"threadId。有的同學(xué)可能會問,這樣不是很縝密:不同的JVM可能會生成相同的threadId,所以Redission這里加了一個區(qū)分度很高的UUID;

Lua腳本中的執(zhí)行分為以下三步:

1:exists檢查redis中是否存在鎖名稱;如果不存在,則獲取成功;同時把邏輯鎖名稱KEYS[1],線程級別的鎖名稱[ARGV[2],value=1,設(shè)置到redis。并設(shè)置邏輯鎖名稱的過期時間ARGV[2],返回;

2:如果檢查到存在KEYS[1],[ARGV[2],則說明獲取成功,此時會自增對應(yīng)的value值,記錄重入次數(shù);并更新鎖的過期時間

3:key不存,直接返回key的剩余過期時間(-2)

相關(guān)文章

  • java HashMap通過value反查key的代碼示例

    java HashMap通過value反查key的代碼示例

    本文講解了java HashMap通過value反查key的方法,直接提供代碼供大家參考使用
    2013-11-11
  • Spring Boot 多個定時器沖突問題的解決方法

    Spring Boot 多個定時器沖突問題的解決方法

    這篇文章主要介紹了Spring Boot 多個定時器沖突問題的解決方法,實際開發(fā)中定時器需要解決多個定時器同時并發(fā)的問題,也要解決定時器之間的沖突問題,本文通過問題場景重現(xiàn)給大家介紹的非常詳細,需要的朋友參考下吧
    2022-05-05
  • java圖片識別文字的方法

    java圖片識別文字的方法

    這篇文章主要為大家詳細介紹了java圖片識別文字的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • Java中對象的創(chuàng)建和銷毀過程詳析

    Java中對象的創(chuàng)建和銷毀過程詳析

    這篇文章主要介紹了Java中對象的創(chuàng)建和銷毀過程,對象的創(chuàng)建過程包括類加載檢查、內(nèi)存分配、初始化零值內(nèi)存、設(shè)置對象頭和執(zhí)行init方法,對象的銷毀過程由垃圾回收機制負責,文中介紹的非常詳細,需要的朋友可以參考下
    2025-02-02
  • Java基數(shù)排序radix sort原理及用法解析

    Java基數(shù)排序radix sort原理及用法解析

    這篇文章主要介紹了Java基數(shù)排序radix sort原理及用法解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • SpringCloud中的路由網(wǎng)關(guān)鑒權(quán)熔斷詳解

    SpringCloud中的路由網(wǎng)關(guān)鑒權(quán)熔斷詳解

    這篇文章主要介紹了SpringCloud中的路由網(wǎng)關(guān)鑒權(quán)熔斷詳解,Hystrix是一個用于處理分布式系統(tǒng)的延遲和容錯的開源庫,在分布式系統(tǒng)里,許多依賴不可避免的會調(diào)用失敗,比如超時、異常等,需要的朋友可以參考下
    2024-01-01
  • Java撲克牌速算24的方法

    Java撲克牌速算24的方法

    這篇文章主要為大家詳細介紹了Java撲克牌速算24的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • SpringBoot超詳細講解@Enable*注解和@Import

    SpringBoot超詳細講解@Enable*注解和@Import

    這篇文章主要介紹了SpringBoot?@Enable*注解和@Import,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 基于HttpClient上傳文件中文名亂碼的解決

    基于HttpClient上傳文件中文名亂碼的解決

    這篇文章主要介紹了HttpClient上傳文件中文名亂碼的解決方案,具有很好的參考價值,希望對大家有所幫助。
    2021-07-07
  • IDEA與JDK、Maven安裝配置完整步驟解析

    IDEA與JDK、Maven安裝配置完整步驟解析

    這篇文章主要介紹了如何安裝和配置IDE(IntelliJ?IDEA),包括IDE的安裝步驟、JDK的下載與配置、Maven的安裝與配置,以及如何在IDE中使用Maven進行Java開發(fā),需要的朋友可以參考下
    2025-03-03

最新評論

北流市| 昌邑市| 江安县| 泽普县| 怀宁县| 郁南县| 许昌县| 大名县| 孝义市| 南郑县| 平阴县| 高雄市| 玉环县| 黄石市| 阿勒泰市| 平顶山市| 雷山县| 深州市| 濉溪县| 绥化市| 新疆| 墨江| 东辽县| 宁夏| 揭阳市| 五莲县| 石渠县| 静宁县| 扶余县| 义乌市| 定西市| 永修县| 泊头市| 横峰县| 山东省| 仲巴县| 西昌市| 乌兰浩特市| 连州市| 广昌县| 永胜县|