博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
聊聊redisson的RMap的computeIfAbsent操作
阅读量:7267 次
发布时间:2019-06-29

本文共 4971 字,大约阅读时间需要 16 分钟。

  hot3.png

本文主要研究一下redisson的RMap的computeIfAbsent操作

实例

@Test    public void testRMapComputeIfAbsent(){        Config config = new Config();        config.useSingleServer()                .setAddress("redis://192.168.99.100:6379");        RedissonClient redisson = Redisson.create(config);        RMap
map = redisson.getMap("anyMap"); CityInfo bj = CityInfo.builder().name("bj").build(); CityInfo currentObject = map.computeIfAbsent("bj", k -> bj); Assert.assertEquals(bj.toString(),currentObject.toString()); }

源码分析

ConcurrentMap.computeIfAbsent

java/util/concurrent/ConcurrentMap.java

/**     * {@inheritDoc}     *     * @implSpec     * The default implementation is equivalent to the following steps for this     * {@code map}, then returning the current value or {@code null} if now     * absent:     *     * 
 {@code     * if (map.get(key) == null) {     *     V newValue = mappingFunction.apply(key);     *     if (newValue != null)     *         return map.putIfAbsent(key, newValue);     * }     * }
* * The default implementation may retry these steps when multiple * threads attempt updates including potentially calling the mapping * function multiple times. * *

This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is * absent. Implementations which support null values must * override this default implementation. * * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} * @since 1.8 */ @Override default V computeIfAbsent(K key, Function

mappingFunction) { Objects.requireNonNull(mappingFunction); V v, newValue; return ((v = get(key)) == null && (newValue = mappingFunction.apply(key)) != null && (v = putIfAbsent(key, newValue)) == null) ? newValue : v; }

  • computeIfAbsent当该key不存在时,返回的是新值,而非null
  • computeIfAbsent方法里头调用了putIfAbsent

RedissonMap.putIfAbsent

redisson-3.8.1-sources.jar!/org/redisson/RedissonMap.java

@Override    public V putIfAbsent(K key, V value) {        return get(putIfAbsentAsync(key, value));    }    @Override    public RFuture
putIfAbsentAsync(final K key, final V value) { checkKey(key); checkValue(key); RFuture
future = putIfAbsentOperationAsync(key, value); if (hasNoWriter()) { return future; } MapWriterTask
listener = new MapWriterTask
() { @Override public void execute() { options.getWriter().write(key, value); } @Override protected boolean condition(Future
future) { return future.getNow() == null; } }; return mapWriterFuture(future, listener); } protected boolean hasNoWriter() { return options == null || options.getWriter() == null; } protected RFuture
putIfAbsentOperationAsync(K key, V value) { return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_MAP_VALUE, "if redis.call('hsetnx', KEYS[1], ARGV[1], ARGV[2]) == 1 then " + "return nil " + "else " + "return redis.call('hget', KEYS[1], ARGV[1]) " + "end", Collections.
singletonList(getName(key)), encodeMapKey(key), encodeMapValue(value)); } protected
RFuture
mapWriterFuture(RFuture
future, final MapWriterTask
listener) { if (options != null && options.getWriteMode() == WriteMode.WRITE_BEHIND) { future.addListener(new MapWriteBehindListener
(commandExecutor, listener, writeBehindCurrentThreads, writeBehindTasks, options.getWriteBehindThreads())); return future; } final RPromise
promise = new RedissonPromise
(); future.addListener(new FutureListener
() { @Override public void operationComplete(final Future
future) throws Exception { if (!future.isSuccess()) { promise.tryFailure(future.cause()); return; } if (listener.condition(future)) { commandExecutor.getConnectionManager().getExecutor().execute(new Runnable() { @Override public void run() { try { listener.execute(); } catch (Exception e) { promise.tryFailure(e); return; } promise.trySuccess(future.getNow()); } }); } else { promise.trySuccess(future.getNow()); } } }); return promise; }
  • RedissonMap覆盖了putIfAbsent方法,这里调用的是putIfAbsentAsync,该方法主要调用putIfAbsentOperationAsync
  • putIfAbsentOperationAsync使用了一段lua脚本来保证原子性,如果hsetnx之前的key不存在且设置成功则返回nil,否则查找已有的值返回
  • putIfAbsentAsync除了调用putIfAbsentOperationAsync,还增加了writer的逻辑,如果有设置writer,则会在putIfAbsentOperationAsync的future成功时回调触发writer
  • writer主要用于外部存储用,比如旁路存储一份到本地磁盘,有同步操作及异步操作两种模式

小结

redisson对redis的数据结构操作进行了更进一步的封装,比如redisson的RMap实现了java.util.concurrent.ConcurrentMap接口和java.util.Map接口,实现了诸如putIfAbsent的方法,用lua脚本在服务端保证了操作的原子性。

doc

转载于:https://my.oschina.net/go4it/blog/2208610

你可能感兴趣的文章