Redis系列-生產應用篇-分散式鎖(5)-單程序Redis分散式鎖的Java實現(Redisson使用與底層實現)-原子鎖類
阿新 • • 發佈:2018-12-10
Redisson單程序Redis分散式樂觀鎖的使用與實現
本文基於Redisson 3.7.5
4. 原子鎖類
Redisson中實現了兩種原子鎖類:RAtomicLong和RAtomicDouble,還有RLongAdder和RDoubleAdder
RAtomicDouble和RAtomicLong其實一樣的,RLongAdder和RDoubleAdder其實原理也是一樣的,這裡我們只說RAtomicLong和RLongAdder。
4.1. RedissonAtomicLong - 基於Redis實現的原子Long類
原子類的incrementAndGet,decrementAndGet,addandGet,主要通過INCR,DECR,INCRBY,DECRBY實現,其實redis的這些操作本身就是原子性的。
@Override
public RFuture<Long> getAndAddAsync(final long delta) {
//getAndAdd通過INCRBY實現
return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, new RedisStrictCommand<Long>("INCRBY", new SingleConvertor<Long>() {
@Override
public Long convert(Object obj) {
return ((Long) obj) - delta;
}
}), getName(), delta);
}
@Override
public RFuture<Long> getAndSetAsync(long newValue) {
//getAndSet通過GetSet實現
return commandExecutor.writeAsync(getName(), LongCodec.INSTANCE, RedisCommands.GETSET, getName(), newValue);
}
@Override
public RFuture<Long> incrementAndGetAsync() {
//incrementAndGet通過INCR實現
return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.INCR, getName());
}
@Override
public RFuture<Long> decrementAndGetAsync() {
//減一通過DECR實現
return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.DECR, getName());
}
那麼CAS更新呢?可以利用lua指令碼的特性,也就是因為redis是單執行緒的,同時只能處理一個lua指令碼,所以lua指令碼具有原子性。
@Override
public RFuture<Boolean> compareAndSetAsync(long expect, long update) {
//CAS操作
//通過lua指令碼的特性實現,lua指令碼的原子性
//先檢查值是否符合,如果符合再更新,返回true,否則返回false
return commandExecutor.evalWriteAsync(getName(), StringCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"local currValue = redis.call('get', KEYS[1]); "
+ "if currValue == ARGV[1] "
+ "or (tonumber(ARGV[1]) == 0 and currValue == false) then "
+ "redis.call('set', KEYS[1], ARGV[2]); "
+ "return 1 "
+ "else "
+ "return 0 "
+ "end",
Collections.<Object>singletonList(getName()), expect, update);
}
4.2. RedissonLongAdder 基於Redis實現的LongAdder
在統計場景下(寫多讀少,且數值不用考慮併發安全),LongAdder表現比AtomicLong更好,那麼基於redis是怎麼實現呢?
Redisson的實現思路比較簡單,本地留存一個longAdder,只有呼叫get或者sum的時候,才把本地的longAdder的數值加到redis中。
public class RedissonLongAdder extends RedissonBaseAdder<Long> implements RLongAdder {
//利用RAtomicLong實現redis中儲存的數值
private final RAtomicLong atomicLong;
//本地longAdder
private final LongAdder counter = new LongAdder();
}
統計但不get的操作都是對於本地longAdder操作:
@Override
public void add(long x) {
counter.add(x);
}
@Override
public void increment() {
add(1L);
}
@Override
public void decrement() {
add(-1L);
}
與get還有sum相關的操作會把本地longAdder的數值加到redis中:
@Override
protected RFuture<Long> addAndGetAsync() {
return atomicLong.getAndAddAsync(counter.sum());
}
@Override
protected RFuture<Long> getAndDeleteAsync() {
return atomicLong.getAndDeleteAsync();
}
@Override
public long sum() {
return get(sumAsync());
}