1. 程式人生 > >Redis系列-生產應用篇-分散式鎖(5)-單程序Redis分散式鎖的Java實現(Redisson使用與底層實現)-原子鎖類

Redis系列-生產應用篇-分散式鎖(5)-單程序Redis分散式鎖的Java實現(Redisson使用與底層實現)-原子鎖類

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());
}