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