com.github.staslev.concurrent.AtomicMapOperationsForImmutableValues Maven / Gradle / Ivy
package com.github.staslev.concurrent;
import java.util.concurrent.ConcurrentMap;
/**
*
* Provides atomic non blocking operations on maps with (conceptually) immutable values, i.e., they will be replaced
* with new instances upon updating their value, rather than mutating the exiting value in place.
*
*/
public class AtomicMapOperationsForImmutableValues {
AtomicMapOperationsForImmutableValues() {
}
private boolean atomicallyInsertNewEntry(final ConcurrentMap map, final K key, final V previousValueOrNull, final V nextValue) {
boolean newKeyInserted;
try {
newKeyInserted = previousValueOrNull == null && map.putIfAbsent(key, nextValue) == null;
} catch (AssertionError assertionError) {
// a defensive catch against an assertion error occasionally raised by the NonBlockingHashMap class
// should probably be looked into sometime, and ideally removed.
newKeyInserted = false;
}
return newKeyInserted;
}
private boolean atomicallyUpdateExistingEntry(final ConcurrentMap map, final K key, final V previousValue, final V nextValue) {
boolean exitingBucketIncreased;
try {
exitingBucketIncreased = previousValue != null && map.replace(key, previousValue, nextValue);
} catch (AssertionError assertionError) {
// a defensive catch against an assertion error occasionally raised by the NonBlockingHashMap class
// should probably be looked into sometime, and ideally removed.
exitingBucketIncreased = false;
}
return exitingBucketIncreased;
}
/**
* Atomically sets the value of key
to the result of applying the oldOrNullToNewValueTransformer
* REGARDLESS of whether key
WAS, OR WAS NOT, present in the map.
*
* @param map The map to perform the operation on.
* @param key The key whose value is to be put or transformed.
* @param oldOrNullToNewValueTransformer The transform function to be applied in order to produce values for the given key.
* This function WILL BE PASSED NULL in case the given key was not present in the map,
* in which case it is expected to produce an initial value. Should be thread safe.
* @param The type of the keys.
* @param The type of the values.
*/
public void putOrTransform(final ConcurrentMap map, final K key,
final NonBlockingOperations.Transformer oldOrNullToNewValueTransformer) {
V previousValueOrNull;
V nextValue;
do {
previousValueOrNull = map.get(key);
nextValue = oldOrNullToNewValueTransformer.transform(previousValueOrNull);
} while (!atomicallyUpdateExistingEntry(map, key, previousValueOrNull, nextValue)
&& !atomicallyInsertNewEntry(map, key, previousValueOrNull, nextValue));
}
/**
* Atomically sets the value of key
to the result of applying the aggregator
* REGARDLESS of whether key
WAS, OR WAS NOT, present in the map.
*
* @param map The map to perform the operation on.
* @param key The key whose value is to be put or transformed.
* @param aggregator The aggregator responsible for producing a transformed (aggregated) value given new input. Should be thread safe.
* @param input The input passed to the aggregator to produce a value.
* @param The type of the keys.
* @param The type of the values.
* @param The type of the input.
*/
public void putOrAggregate(final ConcurrentMap map, final K key, final NonBlockingOperations.Aggregator aggregator, T input) {
V previousValueOrNull;
V nextValue;
do {
previousValueOrNull = map.get(key);
nextValue = aggregator.aggregate(input, previousValueOrNull);
} while (!atomicallyUpdateExistingEntry(map, key, previousValueOrNull, nextValue)
&& !atomicallyInsertNewEntry(map, key, previousValueOrNull, nextValue));
}
/**
* In case key
IS ALREADY PRESENT in the map, atomically sets its value to the value produced by applying the aggregator
.
* In case the given key was not present in the map, no action will take place.
*
* @param map The map to perform the operation on.
* @param key The key whose value is to be put or transformed, in case the given key already present in the map.
* @param aggregator The aggregator responsible for producing a transformed (aggregated) value given new input. Should be thread safe.
* @param input The input passed to the aggregator to produce a value.
* @param The type of the keys.
* @param The type of the values.
* @param The type of the input.
* @return true if the value of the given key was transformed, otherwise (if key was not present), false.
*/
public boolean aggregateIfPresent(final ConcurrentMap map, final K key, final NonBlockingOperations.Aggregator aggregator,
T input) {
V previousValue;
V nextValue;
do {
previousValue = map.get(key);
if (previousValue != null) {
nextValue = aggregator.aggregate(input, previousValue);
} else {
return false;
}
} while (!atomicallyUpdateExistingEntry(map, key, previousValue, nextValue));
return true;
}
/**
* In case key
IS ALREADY PRESENT in the map, atomically sets its value to the value produced by applying the oldOrNullToNewValueTransformer
.
* In case the given key was not present in the map, no action will take place.
*
* @param map The map to perform the operation on.
* @param key The key whose value is to be put or transformed, in case the given key already present in the map.
* @param nonNullOldToNewValueTransformer The transform function to be applied on an EXISTING value corresponding to the given key.
* This function WILL NOT BE PASSED NULL values. Should be thread safe.
* @param The type of the keys.
* @param The type of the values.
* @return true if the value of the given key was transformed, otherwise (if key was not present), false.
*/
public boolean transformIfPresent(final ConcurrentMap map, final K key,
final NonBlockingOperations.Transformer nonNullOldToNewValueTransformer) {
V previousValue;
V nextValue;
do {
previousValue = map.get(key);
if (previousValue != null) {
nextValue = nonNullOldToNewValueTransformer.transform(previousValue);
} else {
return false;
}
} while (!atomicallyUpdateExistingEntry(map, key, previousValue, nextValue));
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy