sequence

This paper focuses on the computeIfAbsent operation of Redisson’s RMap

The instance

    @Test
    public void testRMapComputeIfAbsent(){
        Config config = new Config();
        config.useSingleServer()
                .setAddress("Redis: / / 192.168.99.100:6379");
        RedissonClient redisson = Redisson.create(config);
        RMap<String, CityInfo> map = redisson.getMap("anyMap");

        CityInfo bj = CityInfo.builder().name("bj").build();
        CityInfo currentObject = map.computeIfAbsent("bj", k -> bj);
        Assert.assertEquals(bj.toString(),currentObject.toString());
    }
Copy the code

Source code analysis

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:
     *
     * <pre> {@code
     * if (map.get(key) == null) {
     *     V newValue = mappingFunction.apply(key);
     *     if(newValue ! = null) *return map.putIfAbsent(key, newValue);
     * }
     * }</pre>
     *
     * The default implementation may retry these steps when multiple
     * threads attempt updates including potentially calling the mapping
     * functionmultiple times. * * <p>This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()}  returning null unambiguously means the key is * absent. Implementationswhich support null values <strong>must</strong>
     * override this default implementation.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     * @since 1.8
     */
    @Override
    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v, newValue;
        return((v = get(key)) == null && (newValue = mappingFunction.apply(key)) ! = null && (v = putIfAbsent(key, newValue)) == null) ? newValue : v; }Copy the code
  • ComputeIfAbsent If the key does not exist, a new value is returned instead of NULL
  • The computeIfAbsent method calls 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<V> putIfAbsentAsync(final K key, final V value) {
        checkKey(key);
        checkValue(key);
        
        RFuture<V> future = putIfAbsentOperationAsync(key, value);
        if (hasNoWriter()) {
            return future;
        }
        
        MapWriterTask<V> listener = new MapWriterTask<V>() {
            @Override
            public void execute() {
                options.getWriter().write(key, value);
            }
            
            @Override
            protected boolean condition(Future<V> future) {
                returnfuture.getNow() == null; }};return mapWriterFuture(future, listener);
    }

    protected boolean hasNoWriter() {
        return options == null || options.getWriter() == null;
    }

    protected RFuture<V> 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.<Object>singletonList(getName(key)), encodeMapKey(key), encodeMapValue(value));
    }

    protected <M> RFuture<M> mapWriterFuture(RFuture<M> future, final MapWriterTask<M> listener) {
        if(options ! = null && options.getWriteMode() == WriteMode.WRITE_BEHIND) { future.addListener(new MapWriteBehindListener<M>(commandExecutor, listener, writeBehindCurrentThreads, writeBehindTasks, options.getWriteBehindThreads()));
            return future;
        }        

        final RPromise<M> promise = new RedissonPromise<M>();
        future.addListener(new FutureListener<M>() {
            @Override
            public void operationComplete(final Future<M> 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;
    }
Copy the code
  • Covers RedissonMap putIfAbsent method, this call is putIfAbsentAsync, putIfAbsentOperationAsync main call this method
  • PutIfAbsentOperationAsync using a lua script to ensure atomicity, if before hsetnx key does not exist and sets a successful return nil, otherwise find existing value is returned
  • PutIfAbsentAsync putIfAbsentOperationAsync in addition to the call, but also increased the writer’s logic, if you have set the writer, will in future success callback putIfAbsentOperationAsync trigger writer
  • Writer is mainly used for external storage, for example, off-line storage of a copy to a local disk. Writer can be operated synchronously or asynchronously

summary

Redisson redis data structure operations for the further encapsulation, such as the RMap redisson implements the Java. Util. Concurrent. The ConcurrentMap interface and Java. Util. The Map interface, Methods such as putIfAbsent are implemented to ensure atomicity of operations on the server side with Lua scripts.

doc

  • 7. Distributed collections
  • Rui Gu: The road to building an open source enterprise Redis client