Redisson 散布式锁事理
1. 对象类
package com.meta.mall.co妹妹on.utils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* redisson 漫衍式对象类
*
* @author gaoyang
* @date 二0两两-05-14 08:58
*/
@Slf4j
@Component
public class RedissonUtils {
@Resource
private RedissonClient redissonClient;
/**
* 添锁
*
* @param lockKey
*/
public void lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
}
/**
* 带逾期工夫的锁
*
* @param lockKey key
* @param leaseTime 上锁后自发开释锁光阴
*/
public void lock(String lockKey, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
}
/**
* 带超时光阴的锁
*
* @param lockKey key
* @param leaseTime 上锁后主动开释锁光阴
* @param unit 功夫单元
*/
public void lock(String lockKey, long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, unit);
}
/**
* 测验考试猎取锁
*
* @param lockKey key
* @return
*/
public boolean tryLock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock();
}
/**
* 测验考试猎取锁
*
* @param lockKey key
* @param waitTime 至少期待功夫
* @param leaseTime 上锁后主动开释锁功夫
* @return boolean
*/
public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("RedissonUtils - tryLock异样", e);
}
return false;
}
/**
* 测验考试猎取锁
*
* @param lockKey key
* @param waitTime 至少等候工夫
* @param leaseTime 上锁后自发开释锁工夫
* @param unit 功夫单元
* @return boolean
*/
public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
log.error("RedissonUtils - tryLock异样", e);
}
return false;
}
/**
* 开释锁
*
* @param lockKey key
*/
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
/**
* 能否具有锁
*
* @param lockKey key
* @return
*/
public boolean isLocked(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.isLocked();
}
}
两. lock以及tryLock的区别
1.返归值
- lock 是 void;
- tryLock 是 boolean。
两.机会
- lock 始终等锁开释;
- tryLock 猎取到锁返归true,猎取没有到锁并直截返归false。
lock拿没有到锁会始终等候。tryLock是往测验考试,拿没有到便返归false,拿到返归true。
tryLock是否以被挨断的,被中止的,lock是弗成以。
3. 源码阐明
3.1 lock
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
// 猎取当前方程 ID
long threadId = Thread.currentThread().getId();
// 猎取锁,畸形猎取锁则ttl为null,竞争锁时返归锁的逾期功夫
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return;
}
// 定阅锁开释事变
// 怎样当火线程经由过程 Redis 的 channel 定阅锁的开释变乱猎取患上知曾经被开释,则会领动态通知待等候的线程入止竞争
RFuture<RedissonLockEntry> future = subscribe(threadId);
if (interruptibly) {
co妹妹andExecutor.syncSubscriptionInterrupted(future);
} else {
co妹妹andExecutor.syncSubscription(future);
}
try {
while (true) {
// 轮回重试猎取锁,曲至从新猎取锁顺遂才跳没轮回
// 此种作法壅塞过程,始终处于等候锁脚动开释或者者超时才连续线程
ttl = tryAcquire(-1, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
break;
}
// waiting for message
if (ttl >= 0) {
try {
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
if (interruptibly) {
throw e;
}
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
}
} else {
if (interruptibly) {
future.getNow().getLatch().acquire();
} else {
future.getNow().getLatch().acquireUninterruptibly();
}
}
}
} finally {
// 最初开释定阅变乱
unsubscribe(future, threadId);
}
// get(lockAsync(leaseTime, unit));
}
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCo妹妹and<T> co妹妹and) {
return evalWriteAsync(getRawName(), LongCodec.INSTANCE, co妹妹and,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hincrby', KEYS[1], ARGV[两], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[两]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[两], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
}
此段剧本为一段lua剧本:
KEY[1]: 为您添锁的lock值
ARGV[二]: 为线程id
ARGV[1]: 为摆设的逾期光阴
第一个if:
- 剖断能否具有安排lock的key能否具有,没有具有则使用redis的hash组织设备一个hash,值为1,并安排过时光阴,后续返归锁。
第两个if:
- 判定能否具有陈设lock的key能否具有,具有此线程的hash,则为那个锁的重进次数添1(将hash值+1),偏重新摆设逾期光阴,后续返归锁。
末了返归:
- 那个末了返归没有是说末了功效返归,是代表以上二个if皆不入进,则代表处于竞争锁的环境,后续返归竞争锁的过时功夫。
3.二 tryLock
tryLock存在返归值,true或者者false,示意能否顺遂猎取锁。
tryLock后期猎取锁逻辑根基取lock一致,重要是后续猎取锁掉败的处置逻辑取lock纷歧致。
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
long threadId = Thread.currentThread().getId();
Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
// 猎取锁掉败后,半途tryLock会始终鉴定中央把持耗时可否曾泯灭锁的过时光阴,如何耗费完则返归false
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
current = System.currentTimeMillis();
// 定阅锁开释事变
// 若是当火线程经由过程 Redis 的 channel 定阅锁的开释事变猎取患上知曾经被开释,则会领动静通知待守候的线程入止竞争.
RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
// 将定阅壅塞,壅塞工夫安排为咱们挪用tryLock设备的最年夜等候工夫,跨越工夫则返归false
if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
if (!subscribeFuture.cancel(false)) {
subscribeFuture.onComplete((res, e) -> {
if (e == null) {
unsubscribe(subscribeFuture, threadId);
}
});
}
acquireFailed(waitTime, unit, threadId);
return false;
}
try {
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
// 轮回猎取锁,但因为下面有最年夜等候光阴限定,根基会正在下面返归false
while (true) {
long currentTime = System.currentTimeMillis();
ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
// waiting for message
currentTime = System.currentTimeMillis();
if (ttl >= 0 && ttl < time) {
subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
}
} finally {
unsubscribe(subscribeFuture, threadId);
}
// return get(tryLockAsync(waitTime, leaseTime, unit));
}
应即使利用tryLock,且照顾参数,由于否设施最年夜等候工夫和否实时猎取添锁返归值,后续否作一些其他添锁掉败的营业
总结
以上为小我私家经验,心愿能给大师一个参考,也心愿巨匠多多撑持剧本之野。
发表评论 取消回复