从新的3.x版本后的redisson的源码中可以找到为锁续期创建的定时任务,但是在老的1.2.1中并没有找到定时任务的创建,所以redisson的锁自动续期能力是后来版本引入的嘛?还是策略有所变化。
3.11版本
private void scheduleExpirationRenewal(long threadId) {
RedissonLock.ExpirationEntry entry = new RedissonLock.ExpirationEntry();
RedissonLock.ExpirationEntry oldEntry = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.putIfAbsent(this.getEntryName(), entry);
if (oldEntry != null) {
oldEntry.addThreadId(threadId);
} else {
entry.addThreadId(threadId);
this.renewExpiration();
}
}
private void renewExpiration() {
RedissonLock.ExpirationEntry ee = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());
if (ee != null) {
Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
public void run(Timeout timeout) throws Exception {
RedissonLock.ExpirationEntry ent = (RedissonLock.ExpirationEntry)RedissonLock.EXPIRATION_RENEWAL_MAP.get(RedissonLock.this.getEntryName());
if (ent != null) {
Long threadId = ent.getFirstThreadId();
if (threadId != null) {
RFuture<Boolean> future = RedissonLock.this.renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", e);
} else {
if (res) {
RedissonLock.this.renewExpiration();
}
}
});
}
}
}
}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
}
1.2.1版本,RedissonLock的全部源码
package org.redisson;
import com.lambdaworks.redis.RedisAsyncConnection;
import com.lambdaworks.redis.RedisConnection;
import com.lambdaworks.redis.pubsub.RedisPubSubAdapter;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import org.redisson.async.ResultOperation;
import org.redisson.async.SyncOperation;
import org.redisson.connection.ConnectionManager;
import org.redisson.core.RLock;
public class RedissonLock extends RedissonObject implements RLock {
private final UUID id;
private static final Integer unlockMessage = 0;
private static final ConcurrentMap<String, RedissonLockEntry> ENTRIES = new ConcurrentHashMap();
protected RedissonLock(ConnectionManager connectionManager, String name, UUID id) {
super(connectionManager, name);
this.id = id;
}
private void unsubscribe() {
RedissonLockEntry entry;
RedissonLockEntry newEntry;
do {
entry = (RedissonLockEntry)ENTRIES.get(this.getEntryName());
if (entry == null) {
return;
}
newEntry = new RedissonLockEntry(entry);
newEntry.release();
} while(!ENTRIES.replace(this.getEntryName(), entry, newEntry));
if (newEntry.isFree() && ENTRIES.remove(this.getEntryName(), newEntry)) {
synchronized(ENTRIES) {
if (!ENTRIES.containsKey(this.getEntryName())) {
this.connectionManager.unsubscribe(this.getChannelName());
}
}
}
}
private String getEntryName() {
return this.id + ":" + this.getName();
}
private Promise<Boolean> aquire() {
RedissonLockEntry entry;
RedissonLockEntry newEntry;
do {
entry = (RedissonLockEntry)ENTRIES.get(this.getEntryName());
if (entry == null) {
return null;
}
newEntry = new RedissonLockEntry(entry);
newEntry.aquire();
} while(!ENTRIES.replace(this.getEntryName(), entry, newEntry));
return newEntry.getPromise();
}
private Future<Boolean> subscribe() {
Promise<Boolean> promise = this.aquire();
if (promise != null) {
return promise;
} else {
Promise<Boolean> newPromise = this.newPromise();
final RedissonLockEntry value = new RedissonLockEntry(newPromise);
value.aquire();
RedissonLockEntry oldValue = (RedissonLockEntry)ENTRIES.putIfAbsent(this.getEntryName(), value);
if (oldValue != null) {
Promise<Boolean> oldPromise = this.aquire();
return (Future)(oldPromise == null ? this.subscribe() : oldPromise);
} else {
RedisPubSubAdapter<Object> listener = new RedisPubSubAdapter<Object>() {
public void subscribed(String channel, long count) {
if (RedissonLock.this.getChannelName().equals(channel) && !value.getPromise().isSuccess()) {
value.getPromise().setSuccess(true);
}
}
public void message(String channel, Object message) {
if (message.equals(RedissonLock.unlockMessage) && RedissonLock.this.getChannelName().equals(channel)) {
value.getLatch().release();
}
}
};
synchronized(ENTRIES) {
this.connectionManager.subscribe(listener, this.getChannelName());
return newPromise;
}
}
}
}
private String getChannelName() {
return "redisson__lock__channel__{" + this.getName() + "}";
}
public void lock() {
try {
this.lockInterruptibly();
} catch (InterruptedException var2) {
Thread.currentThread().interrupt();
}
}
public void lock(long leaseTime, TimeUnit unit) {
try {
this.lockInterruptibly(leaseTime, unit);
} catch (InterruptedException var5) {
Thread.currentThread().interrupt();
}
}
public void lockInterruptibly() throws InterruptedException {
this.lockInterruptibly(-1L, (TimeUnit)null);
}
// 在这里找不到锁续期的代码
public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
Long ttl;
if (leaseTime != -1L) {
ttl = this.tryLockInner(leaseTime, unit);
} else {
ttl = this.tryLockInner();
}
if (ttl != null) {
// 这个方法实际的作用看不太懂,比较复杂
this.subscribe().awaitUninterruptibly();
try {
while(true) {
if (leaseTime != -1L) {
ttl = this.tryLockInner(leaseTime, unit);
} else {
ttl = this.tryLockInner();
}
if (ttl == null) {
return;
}
RedissonLockEntry entry = (RedissonLockEntry)ENTRIES.get(this.getEntryName());
if (ttl >= 0L) {
entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
entry.getLatch().acquire();
}
}
} finally {
this.unsubscribe();
}
}
}
public boolean tryLock() {
return this.tryLockInner() == null;
}
private Long tryLockInner() {
final RedissonLock.LockValue currentLock = new RedissonLock.LockValue(this.id, Thread.currentThread().getId());
currentLock.incCounter();
return (Long)this.connectionManager.write(this.getName(), new SyncOperation<RedissonLock.LockValue, Long>() {
public Long execute(RedisConnection<Object, RedissonLock.LockValue> connection) {
Boolean res = connection.setnx(RedissonLock.this.getName(), currentLock);
if (!res) {
connection.watch(new Object[]{RedissonLock.this.getName()});
RedissonLock.LockValue lock = (RedissonLock.LockValue)connection.get(RedissonLock.this.getName());
if (lock != null && lock.equals(currentLock)) {
lock.incCounter();
connection.multi();
connection.set(RedissonLock.this.getName(), lock);
if (connection.exec().size() == 1) {
return null;
}
}
connection.unwatch();
Long ttl = connection.pttl(RedissonLock.this.getName());
return ttl;
} else {
return null;
}
}
});
}
private Long tryLockInner(final long leaseTime, final TimeUnit unit) {
final RedissonLock.LockValue currentLock = new RedissonLock.LockValue(this.id, Thread.currentThread().getId());
currentLock.incCounter();
return (Long)this.connectionManager.write(this.getName(), new SyncOperation<Object, Long>() {
public Long execute(RedisConnection<Object, Object> connection) {
long time = unit.toMillis(leaseTime);
String res = connection.setexnx(RedissonLock.this.getName(), currentLock, time);
if ("OK".equals(res)) {
return null;
} else {
connection.watch(new Object[]{RedissonLock.this.getName()});
RedissonLock.LockValue lock = (RedissonLock.LockValue)connection.get(RedissonLock.this.getName());
if (lock != null && lock.equals(currentLock)) {
lock.incCounter();
connection.multi();
connection.psetex(RedissonLock.this.getName(), time, lock);
if (connection.exec().size() == 1) {
return null;
}
}
connection.unwatch();
Long ttl = connection.pttl(RedissonLock.this.getName());
return ttl;
}
}
});
}
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
Long ttl;
if (leaseTime != -1L) {
ttl = this.tryLockInner(leaseTime, unit);
} else {
ttl = this.tryLockInner();
}
if (ttl == null) {
return true;
} else if (!this.subscribe().awaitUninterruptibly(time, TimeUnit.MILLISECONDS)) {
return false;
} else {
try {
while(true) {
if (leaseTime != -1L) {
ttl = this.tryLockInner(leaseTime, unit);
} else {
ttl = this.tryLockInner();
}
boolean var17;
if (ttl == null) {
var17 = true;
return var17;
}
if (time <= 0L) {
var17 = false;
return var17;
}
long current = System.currentTimeMillis();
RedissonLockEntry entry = (RedissonLockEntry)ENTRIES.get(this.getEntryName());
if (ttl >= 0L && ttl < time) {
entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
entry.getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
}
long elapsed = System.currentTimeMillis() - current;
time -= elapsed;
}
} finally {
this.unsubscribe();
}
}
}
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return this.tryLock(time, -1L, unit);
}
public void unlock() {
this.connectionManager.write(this.getName(), new SyncOperation<Object, Void>() {
public Void execute(RedisConnection<Object, Object> connection) {
RedissonLock.LockValue lock = (RedissonLock.LockValue)connection.get(RedissonLock.this.getName());
if (lock != null) {
RedissonLock.LockValue currentLock = new RedissonLock.LockValue(RedissonLock.this.id, Thread.currentThread().getId());
if (!lock.equals(currentLock)) {
throw new IllegalMonitorStateException("Attempt to unlock lock, not locked by current id: " + RedissonLock.this.id + " thread-id: " + Thread.currentThread().getId());
}
if (lock.getCounter() > 1) {
lock.decCounter();
connection.set(RedissonLock.this.getName(), lock);
} else {
RedissonLock.this.unlock(connection);
}
}
return null;
}
});
}
private void unlock(RedisConnection<Object, Object> connection) {
for(int counter = 0; counter < 5; ++counter) {
connection.multi();
connection.del(new Object[]{this.getName()});
connection.publish(this.getChannelName(), unlockMessage);
List<Object> res = connection.exec();
if (res.size() == 2) {
return;
}
}
throw new IllegalStateException("Can't unlock lock after 5 attempts. Current id: " + this.id + " thread-id: " + Thread.currentThread().getId());
}
public Condition newCondition() {
throw new UnsupportedOperationException();
}
public void forceUnlock() {
this.connectionManager.write(this.getName(), new SyncOperation<Object, Void>() {
public Void execute(RedisConnection<Object, Object> connection) {
RedissonLock.this.unlock(connection);
return null;
}
});
}
public boolean isLocked() {
return this.getCurrentLock() != null;
}
private RedissonLock.LockValue getCurrentLock() {
RedissonLock.LockValue lock = (RedissonLock.LockValue)this.connectionManager.read(this.getName(), new ResultOperation<RedissonLock.LockValue, RedissonLock.LockValue>() {
protected Future<RedissonLock.LockValue> execute(RedisAsyncConnection<Object, RedissonLock.LockValue> async) {
return async.get(RedissonLock.this.getName());
}
});
return lock;
}
public boolean isHeldByCurrentThread() {
RedissonLock.LockValue lock = this.getCurrentLock();
RedissonLock.LockValue currentLock = new RedissonLock.LockValue(this.id, Thread.currentThread().getId());
return lock != null && lock.equals(currentLock);
}
public int getHoldCount() {
RedissonLock.LockValue lock = this.getCurrentLock();
RedissonLock.LockValue currentLock = new RedissonLock.LockValue(this.id, Thread.currentThread().getId());
return lock != null && lock.equals(currentLock) ? lock.getCounter() : 0;
}
public void delete() {
this.forceUnlock();
}
public static class LockValue implements Serializable {
private static final long serialVersionUID = -8895632286065689476L;
private UUID id;
private Long threadId;
private int counter;
public LockValue() {
}
public LockValue(UUID id, Long threadId) {
this.id = id;
this.threadId = threadId;
}
public void decCounter() {
--this.counter;
}
public void incCounter() {
++this.counter;
}
public int getCounter() {
return this.counter;
}
public int hashCode() {
int prime = true;
int result = 1;
int result = 31 * result + (this.id == null ? 0 : this.id.hashCode());
result = 31 * result + (this.threadId == null ? 0 : this.threadId.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (this.getClass() != obj.getClass()) {
return false;
} else {
RedissonLock.LockValue other = (RedissonLock.LockValue)obj;
if (this.id == null) {
if (other.id != null) {
return false;
}
} else if (!this.id.equals(other.id)) {
return false;
}
if (this.threadId == null) {
if (other.threadId != null) {
return false;
}
} else if (!this.threadId.equals(other.threadId)) {
return false;
}
return true;
}
}
}
}