Redisシリーズ-生産応用編-分布式ロック(5)-単一プロセスRedis分布式ロックのJava実装(Redisson使用と下位実装)-原子ロッククラス


Redisson単一プロセスRedis分布式楽観ロックの使用と実現
本論文はRedisson 3.7.5に基づく
4.原子鎖類
Redissonでは、RAtomicLongとRAtomicDouble、RLongAdderとRDoubleAdderの2つの原子ロッククラスが実現されています.
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は単一スレッドであり、同時に1つの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を1つ残し,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());
}