官方文档
https://github.com/redisson/redisson/wiki
中文版:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95
package org.example;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* @author WangChao
*/
public class TestLock {
public static void main(String[] args) throws IOException, InterruptedException {
new Thread(()->{
try {
Thread.sleep(3000);
RLock lock = getrLock();
lock.lock(15, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
RLock lock = getrLock();
lock.lock(50, TimeUnit.SECONDS);
Thread.sleep(7000);
//lock.lockInterruptibly();
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
// boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
lock.unlock();
}
private static RLock getrLock() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("root");
RedissonClient redisson = Redisson.create(config);
return redisson.getLock("myLock");
}
}
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
internalLockLeaseTime = unit.toMillis(leaseTime);
//通过eval命令执行Lua代码完成加锁操作
return evalWriteAsync(getName(), LongCodec.INSTANCE, command,
//判断当前key是否存在,如果不存在
"if (redis.call('exists', KEYS[1]) == 0) then " +
//为value+1操作,如果value不存在则默认1,支持重入
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
//设置过期时间,避免死锁,和 EXPIRE 区别在于毫秒数计数
"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));
}
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
//如果key不存在,返回nil,确保锁只能被持有的释放.
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
"return nil;" +
"end; " +
//锁重入次数-1
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
//>0说明还有重入次数,则重置过期时间
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
//删除锁
"redis.call('del', KEYS[1]); " +
//publish消息,将获取锁被阻塞的线程恢复重新获取锁
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; " +
"end; " +
"return nil;",
Arrays.asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
//获取当前线程id
long threadId = Thread.currentThread().getId();
//加锁
Long ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired
//获取锁成功,直接返回
if (ttl == null) {
return;
}
//加锁失败,则订阅消息,利用redis的pubsub提供一个通知机制,来减少不断重试,避免活锁
RFuture<RedissonLockEntry> future = subscribe(threadId);
if (interruptibly) {
commandExecutor.syncSubscriptionInterrupted(future);
} else {
commandExecutor.syncSubscription(future);
}
try {
while (true) {
ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
break;
}
// waiting for message
if (ttl >= 0) {
try {
//调用Semaphore阻塞
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));
}
/**
* Copyright (c) 2013-2020 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.pubsub;
import org.redisson.RedissonLockEntry;
import org.redisson.misc.RPromise;
/**
*
* @author Nikita Koksharov
*
*/
public class LockPubSub extends PublishSubscribe<RedissonLockEntry> {
public static final Long UNLOCK_MESSAGE = 0L;
public static final Long READ_UNLOCK_MESSAGE = 1L;
public LockPubSub(PublishSubscribeService service) {
super(service);
}
@Override
protected RedissonLockEntry createEntry(RPromise<RedissonLockEntry> newPromise) {
return new RedissonLockEntry(newPromise);
}
@Override
protected void onMessage(RedissonLockEntry value, Long message) {
if (message.equals(UNLOCK_MESSAGE)) {
Runnable runnableToExecute = value.getListeners().poll();
if (runnableToExecute != null) {
runnableToExecute.run();
}
//说明有锁释放,则结束阻塞
value.getLatch().release();
} else if (message.equals(READ_UNLOCK_MESSAGE)) {
while (true) {
Runnable runnableToExecute = value.getListeners().poll();
if (runnableToExecute == null) {
break;
}
runnableToExecute.run();
}
value.getLatch().release(value.getLatch().getQueueLength());
}
}
}
维护一个普通线程,专门监控当前获取到锁的线程是否还存活,如果存活则,延长过期时间,
private void renewExpiration() {
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null) {
return;
}
// 添加一个netty的Timeout回调任务,每(用户设置超时时间 / 3)毫秒执行一次
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null) {
return;
}
//获取第一个线程id
Long threadId = ent.getFirstThreadId();
if (threadId == null) {
return;
}
//重置过期时间,如果key存在,说明这个获取锁的线程还在执行
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
log.error("Can't update lock " + getName() + " expiration", e);
return;
}
if (res) {
// reschedule itself
//递归调用本方法
renewExpiration();
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}