Redis分散ロック-tryLock

5919 ワード

まとめは前に書きます.
RLock rLock = redissonClient.getLock("lbhTestLock");

tryLock無パラメータメソッドを使用すると、redissonは自動的にタイミングタスクを追加し、ロックの失効時間をタイミングでリフレッシュし、unlockで失敗すると、ロックが解放されない場合があります.
tryLockがリリース時間を送信すると、このタイミングタスクは追加されません.
テストは次のとおりです.
1、tryLock  

@Test
public void testLock() throws Exception {
    RLock rLock = redissonClient.getLock("lbhTestLock");
    rLock.tryLock();
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                if (rLock.tryLock()) {
                    System.out.println(" ");
                } else {
                    System.out.println(" ");
                }
                Thread.sleep(35000);
                if (rLock.tryLock()) {
                    System.out.println(" ");
                } else {
                    System.out.println(" ");
                }
            } catch (Exception e) {

            }

        }
    }).start();
    Thread.sleep(40000);
    rLock.unlock();
}

ロック解除されていません
ロック解除されていません
2、tryLockリリース時間
rLock.tryLock(0,30,TimeUnit.SECONDS);

出力:
ロックは正常に取得されませんでした
テスト済み
@Test
public void testLock2() throws Exception {
    new Thread(new Runnable() {
        @Override
        public void run() {
            RLock rLock = redissonClient.getLock("lbhTestLock");
            rLock.tryLock();
        }
    }).start();
    Thread.sleep(600000);
    //rLock.unlock();
}

redisコンソール:
test01:0>ttl lbhTestLock
"29"test01:0>ttl lbhTestLock
"28"test01:0>ttl lbhTestLock
"27"test01:0>ttl lbhTestLock
"25"test01:0>ttl lbhTestLock
"24"test01:0>ttl lbhTestLock
"22"test01:0>ttl lbhTestLock
"21"
//失効時間がtest 01:0>ttl lbhTestLockにリセットされました
"28"
//プログラムクローズ後test 01:0>ttl lbhTestLock
"18"test01:0>ttl lbhTestLock
"15"test01:0>ttl lbhTestLock
"13"
tryLock()を呼び出してunlockまたはunlockがない場合に失敗し、プログラムが閉じない場合、redissonがロックの有効期限をリフレッシュするため、他のスレッドはこのロックを取得しません.ttlはredissonインスタンスが閉じない限り、redissonによって自動的に消去されます.
ソース分析:
tryLock()を呼び出してメソッドがない場合:
@Override
public boolean tryLock() {
    return get(tryLockAsync());
}
@Override
public RFuture tryLockAsync() {
    return tryLockAsync(Thread.currentThread().getId());
}
public RFuture tryLockAsync(long threadId) {
    return tryAcquireOnceAsync(-1, null, threadId);
}
private RFuture tryAcquireOnceAsync(long leaseTime, TimeUnit unit, final long threadId) {
    // 
    if (leaseTime != -1) {
        return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
    }
    //  
    RFuture ttlRemainingFuture = tryLockInnerAsync(LOCK_EXPIRATION_INTERVAL_SECONDS, TimeUnit.SECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
    //  Listener  !
    ttlRemainingFuture.addListener(new FutureListener() {
        @Override
        public void operationComplete(Future future) throws Exception {
            if (!future.isSuccess()) {
                return;
            }

            Boolean ttlRemaining = future.getNow();
            // lock acquired
            //  tryLock , tryLock 
            if (ttlRemaining) {
                //   
                scheduleExpirationRenewal(threadId);
            }
        }
    });
    return ttlRemainingFuture;
}
private void scheduleExpirationRenewal(final long threadId) {
    if (expirationRenewalMap.containsKey(getEntryName())) {
        return;
    }

    Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
        @Override
        public void run(Timeout timeout) throws Exception {
            //  
            RFuture future = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                    "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        // key { }
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return 1; " +
                    "end; " +
                    "return 0;",
                      Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
            
            future.addListener(new FutureListener() {
                @Override
                public void operationComplete(Future future) throws Exception {
                    expirationRenewalMap.remove(getEntryName());
                    if (!future.isSuccess()) {
                        log.error("Can't update lock " + getName() + " expiration", future.cause());
                        return;
                    }
                    
                    if (future.getNow()) {
                        // reschedule itself
                        scheduleExpirationRenewal(threadId);
                    }
                }
            });
        }
    }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);

    if (expirationRenewalMap.putIfAbsent(getEntryName(), task) != null) {
        task.cancel();
    }
}

タイム・タスクの有効期限の1/3(デフォルト30 sの1/310 s)ごとにロックの有効期限をリフレッシュする追加
//luaスクリプト解析
まず、このロックキーのmap構造に対応するキー8 a 9649 f 5-f 5 b 5-48 b 4-beaa-d 0 c 24855 f 9 ab:anyLock:1が存在するかどうかを判断し、存在する場合はpexpireコマンドを直接呼び出してロックキーの有効期限を設定し、デフォルトは30000ミリ秒である.
この原理については、以下を参照してください.https://blog.csdn.net/ice24for/article/details/86177152