Talk about the computeIfAbsent operation of redisson’s RMap.

  redis, redisson


This paper mainly studies the computeIfAbsent operation of redisson’s RMap.


    public void testRMapComputeIfAbsent(){
        Config config = new Config();
        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);

Source code analysis



     * {@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
     * function multiple times.
     * <p>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 <strong>must</strong>
     * override this default implementation.
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     * @since 1.8
    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        V v, newValue;
        return ((v = get(key)) == null &&
                (newValue = mappingFunction.apply(key)) != null &&
                (v = putIfAbsent(key, newValue)) == null) ? newValue : v;
  • ComputeIfAbsent returns a new value instead of null when the key does not exist.
  • PutIfAbsent was called in the computeIfAbsent method.


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

    public V putIfAbsent(K key, V value) {
        return get(putIfAbsentAsync(key, value));

    public RFuture<V> putIfAbsentAsync(final K key, final V value) {
        RFuture<V> future = putIfAbsentOperationAsync(key, value);
        if (hasNoWriter()) {
            return future;
        MapWriterTask<V> listener = new MapWriterTask<V>() {
            public void execute() {
                options.getWriter().write(key, value);
            protected boolean condition(Future<V> future) {
                return future.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'hsetnx', KEYS[1], ARGV[1], ARGV[2]) == 1 then "
                    + "return nil "
                + "else "
                    + "return'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>() {
            public void operationComplete(final Future<M> future) throws Exception {
                if (!future.isSuccess()) {

                if (listener.condition(future)) {
                    commandExecutor.getConnectionManager().getExecutor().execute(new Runnable() {
                        public void run() {
                            try {
                            } catch (Exception e) {
                } else {

        return promise;
  • RedissonMap covers the putIfAbsent method, which calls putIfAbsentAsync here. The method mainly calls putIfAbsentOperationAsync
  • PutIfAbsentOperationAsync uses a lua script to ensure atomicity. if the key before hsetnx does not exist and the setting is successful, nil is returned; otherwise, the existing value is found and returned.
  • PutIfAbsentAsync not only calls putiabsenttoperationasync, but also adds the writer’s logic. if the writer is set, the writer will be triggered by callback when putiabsenttoperationasync’s future is successful.
  • Writer is mainly used for external storage, such as bypass storage of a copy to a local disk. There are two modes of synchronous operation and asynchronous operation.


Redisson further encapsulates redis’ data structure operations. For example, redisson’s RMap implements Java. Util. Concurrent. Concurrent Map interface and java.util.Map interface, implements methods such as putIfAbsent, and uses lua script to ensure the atomicity of operations at the server.