有梦就能飞 2019-06-28
本文主要研究一下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<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()); }
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 * 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 */ @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; }
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) { 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 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; }
redisson对redis的数据结构操作进行了更进一步的封装,比如redisson的RMap实现了java.util.concurrent.ConcurrentMap接口和java.util.Map接口,实现了诸如putIfAbsent的方法,用lua脚本在服务端保证了操作的原子性。