I would like to know what would be the best mechanism to implement multiple Producer - single Consumer scenario, where i have to keep the current number of unprocessed requests up to date.
My first thought was to use ConcurrentLinkedQueue:
public class SomeQueueAbstraction {
private Queue<SomeObject> concurrentQueue = new ConcurrentLinkedQueue<>();
private int size;
public void add(Object request) {
SomeObject object = convertIncomingRequest(request);
concurrentQueue.add(object);
size++;
}
public SomeObject getHead() {
SomeObject object = concurrentQueue.poll();
size--;
}
// other methods
Problem with this is that i have to explicitly synchronize on add and size ++, as well as on the poll and size--, to have always accurate size which makes ConccurentLinkedQueue pointless to begin with.
What would be the best way to achieve as good as possible performance while maintaining data consistency ?
Should I use ArrayDequeue instead and explicitly synchronize or there is a better way to achieve this ?
There is sort of similar question/answer here:
java.util.ConcurrentLinkedQueue
where it is discussed how composite operations on ConcurrentLinkedQueue are naturally not atomic but there is no direct answer what is the best option for the given scenario.
Note: I am calculating size explicitly because time complexity for inherent .size() method is O(n).
Note2: I am also worried that getSize() method, which i haven't explicitly written, will add to even more contention overhead. It could be called relatively frequently.
I am looking for the most efficient way to handle Multiple Producers - single Consumer with frequent getSize() calls.
Alternative suggestion: If there was elementId in SomeObject structure, i could get current size from ConcurrentLinkedQueue.poll() and only locking would have to be done within mechanism to generate such id. Add and get could now properly be used without additional locking. How would this fare as an alternative ?
So the requirement is to report an up to date current number of unprocessed requests. And this is requested often which indeed makes ConcurrentLinkedQueue.size() unsuitable.
This can be done using an AtomicInteger: it is fast and is always as close to the current number of unprocessed requests as possible.
Here is an example, note some small updates to ensure that the reported size is accurate:
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class SomeQueueAbstraction {
private final Queue<SomeObject> concurrentQueue = new ConcurrentLinkedQueue<>();
private final AtomicInteger size = new AtomicInteger();
public boolean add(Object request) {
SomeObject object = convertIncomingRequest(request);
if (concurrentQueue.add(object)) {
size.incrementAndGet();
return true;
}
return false;
}
public SomeObject remove() {
SomeObject object = concurrentQueue.poll();
if (object != null) {
size.decrementAndGet();
}
return object;
}
public int getSize() { return size.get(); }
private SomeObject convertIncomingRequest(Object request) {
return new SomeObject(getSize());
}
class SomeObject {
int id;
SomeObject(int id) { this.id = id; }
}
}
You can use an explicit lock, which means you probably won't need a concurrent queue.
public class SomeQueueAbstraction {
private Queue<SomeObject> queue = new LinkedList<>();
private volatile int size;
private Object lock = new Object();
public void add(Object request) {
SomeObject object = convertIncomingRequest(request);
synchronized(lock) {
queue.add(object);
size++;
}
}
public SomeObject getHead() {
SomeObject object = null;
synchronized(lock) {
object = queue.poll();
size--;
}
return object;
}
public int getSize() {
synchronized(lock) {
return size;
}
}
// other methods
}
This way, adding/removing elements to/from the queue and updating the size will be done safely.
Related
how can I provide synchronization upon method parameter values?
All method calls using the 'same' parameter value A should be synchronized. A method call with a different parameter value e.g. B can access, even when calls with A are already waiting. The next concurrent call for B must wait also for the first B to be released.
My use case: I want to synchronize the access to JPA entities on ID level but want to avoid pessimistic locking because I need kind of a queue. The 'key' for locking is intended to be the entity ID - which is in fact of the type Java Long.
protected void entityLockedAccess(SomeEntity myEntity) {
//getId() returns different Long objects so the lock does not work
synchronized (myEntity.getId()) {
//the critical section ...
}
}
I read about lock objects but I am not sure how they would suit in my case.
On the top level I want to manage a specific REST call to my application which executes critical code.
Thanks,
Chris
As far as I understood you basically want a different, unique lock for each of your SomeEntity IDs.
You could realize this with a Map<Integer, Object>.
You simply map each ID to an object. Should there already be an object, you reuse it. This could look something like this:
static Map<Integer, Object> locks = new ConcurrentHashMap<>();
public static void main(String[] args)
{
int i1 = 1;
int i2 = 2;
foo(i1);
foo(i1);
foo(i2);
}
public static void foo(int o)
{
synchronized (locks.computeIfAbsent(o, k -> new Object()))
{
// computation
}
}
This will create 2 lock objects in the map as the object for i1 is reused in the second foo(i1) call.
Objects which are pooled and potentially reused should not be used for synchronization. If they are, it can cause unrelated threads to deadlock with unhelpful stacktraces.
Specifically, String literals, and boxed primitives such as Integers should NOT be used as lock objects because they are pooled and reused.
The story is even worse for Boolean objects because there are only two instances of Boolean, Boolean.TRUE and Boolean.FALSE and every class that uses a Boolean will be referring to one of the two.
I read about lock objects but I am not sure how they would suit in my
case. On the top level I want to manage a specific REST call to my
application which executes critical code.
You DB will take care for concurrent writes and other transactional issues.
All you need to do is use Transactions.
I would also recommend you to go through the classical problems (DIRTY READs NON Repeatable reads). You can also use Optimistic Locking for
The problem is that you simply should not synchronize on values (for example strings, or Integer objects).
Meaning: you would need to define some special EntityId class here, and of course, all "data" that uses the same ID would somehow need to be using the same EntityId object then.
private static final Set<Integer> lockedIds = new HashSet<>();
private void lock(Integer id) throws InterruptedException {
synchronized (lockedIds) {
while (!lockedIds.add(id)) {
lockedIds.wait();
}
}
}
private void unlock(Integer id) {
synchronized (lockedIds) {
lockedIds.remove(id);
lockedIds.notifyAll();
}
}
public void entityLockedAccess(SomeEntity myEntity) throws InterruptedException {
try {
lock(myEntity.getId());
//Put your code here.
//For different ids it is executed in parallel.
//For equal ids it is executed synchronously.
} finally {
unlock(myEntity.getId());
}
}
id can be not only an 'Integer' but any class with correctly overridden 'equals' and 'hashCode' methods.
try-finally - is very important - you must guarantee to unlock waiting threads after your operation even if your operation threw exception.
It will not work if your back-end is distributed across multiple servers/JVMs.
Just use this class:
(and the map will NOT increase in size over time)
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class SameKeySynchronizer<T> {
private final ConcurrentHashMap<T, Object> sameKeyTasks = new ConcurrentHashMap<>();
public void serializeSameKeys(T key, Consumer<T> keyConsumer) {
// This map will never be filled (because function returns null), it is only used for synchronization purposes for the same key
sameKeyTasks.computeIfAbsent(key, inputArgumentKey -> acceptReturningNull(inputArgumentKey, keyConsumer));
}
private Object acceptReturningNull(T inputArgumentKey, Consumer<T> keyConsumer) {
keyConsumer.accept(inputArgumentKey);
return null;
}
}
Like in this test:
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class SameKeySynchronizerTest {
private static final boolean SHOW_FAILING_TEST = false;
#Test
void sameKeysAreNotExecutedParallel() throws InterruptedException {
TestService testService = new TestService();
TestServiceThread testServiceThread1 = new TestServiceThread(testService, "a");
TestServiceThread testServiceThread2 = new TestServiceThread(testService, "a");
testServiceThread1.start();
testServiceThread2.start();
testServiceThread1.join();
testServiceThread2.join();
Assertions.assertFalse(testService.sameKeyInProgressSimultaneously);
}
#Test
void differentKeysAreExecutedParallel() throws InterruptedException {
TestService testService = new TestService();
TestServiceThread testServiceThread1 = new TestServiceThread(testService, "a");
TestServiceThread testServiceThread2 = new TestServiceThread(testService, "b");
testServiceThread1.start();
testServiceThread2.start();
testServiceThread1.join();
testServiceThread2.join();
Assertions.assertFalse(testService.sameKeyInProgressSimultaneously);
Assertions.assertTrue(testService.differentKeysInProgressSimultaneously);
}
private class TestServiceThread extends Thread {
TestService testService;
String key;
TestServiceThread(TestService testService, String key) {
this.testService = testService;
this.key = key;
}
#Override
public void run() {
testService.process(key);
}
}
private class TestService {
private final SameKeySynchronizer<String> sameKeySynchronizer = new SameKeySynchronizer<>();
private Set<String> keysInProgress = ConcurrentHashMap.newKeySet();
private boolean sameKeyInProgressSimultaneously = false;
private boolean differentKeysInProgressSimultaneously = false;
void process(String key) {
if (SHOW_FAILING_TEST) {
processInternal(key);
} else {
sameKeySynchronizer.serializeSameKeys(key, inputArgumentKey -> processInternal(inputArgumentKey));
}
}
#SuppressWarnings("MagicNumber")
private void processInternal(String key) {
try {
boolean keyInProgress = !keysInProgress.add(key);
if (keyInProgress) {
sameKeyInProgressSimultaneously = true;
}
try {
int sleepTimeInMillis = 100;
for (long elapsedTimeInMillis = 0; elapsedTimeInMillis < 1000; elapsedTimeInMillis += sleepTimeInMillis) {
Thread.sleep(sleepTimeInMillis);
if (keysInProgress.size() > 1) {
differentKeysInProgressSimultaneously = true;
}
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
} finally {
keysInProgress.remove(key);
}
}
}
}
In a Spring-based application I have a service which performs the calculation of some Index. Index is relatively expensive to calculate (say, 1s) but relatively cheap to check for actuality (say, 20ms). Actual code does not matter, it goes along the following lines:
public Index getIndex() {
return calculateIndex();
}
public Index calculateIndex() {
// 1 second or more
}
public boolean isIndexActual(Index index) {
// 20ms or less
}
I'm using Spring Cache to cache the calculated index via #Cacheable annotation:
#Cacheable(cacheNames = CacheConfiguration.INDEX_CACHE_NAME)
public Index getIndex() {
return calculateIndex();
}
We currently configure GuavaCache as cache implementation:
#Bean
public Cache indexCache() {
return new GuavaCache(INDEX_CACHE_NAME, CacheBuilder.newBuilder()
.expireAfterWrite(indexCacheExpireAfterWriteSeconds, TimeUnit.SECONDS)
.build());
}
#Bean
public CacheManager indexCacheManager(List<Cache> caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return cacheManager;
}
What I also need is to check if cached value is still actual and refresh it (ideally asynchronously) if it is not. So ideally it should go as follows:
When getIndex() is called, Spring checks if there is a value in the cache.
If not, new value is loaded via calculateIndex() and stored in the cache
If yes, the existing value is checked for actuality via isIndexActual(...).
If old value is actual, it is returned.
If old value is not actual, it is returned, but removed from the cache and loading of the new value is triggered as well.
Basically I want to serve the value from the cache very fast (even if it is obsolete) but also trigger refreshing right away.
What I've got working so far is checking for actuality and eviction:
#Cacheable(cacheNames = INDEX_CACHE_NAME)
#CacheEvict(cacheNames = INDEX_CACHE_NAME, condition = "target.isObsolete(#result)")
public Index getIndex() {
return calculateIndex();
}
This checks triggers eviction if the result is obsolete and returns the old value immediately even if it is the case. But this does not refresh the value in the cache.
Is there a way to configure Spring Cache to actively refresh obsolete values after eviction?
Update
Here's a MCVE.
public static class Index {
private final long timestamp;
public Index(long timestamp) {
this.timestamp = timestamp;
}
public long getTimestamp() {
return timestamp;
}
}
public interface IndexCalculator {
public Index calculateIndex();
public long getCurrentTimestamp();
}
#Service
public static class IndexService {
#Autowired
private IndexCalculator indexCalculator;
#Cacheable(cacheNames = "index")
#CacheEvict(cacheNames = "index", condition = "target.isObsolete(#result)")
public Index getIndex() {
return indexCalculator.calculateIndex();
}
public boolean isObsolete(Index index) {
long indexTimestamp = index.getTimestamp();
long currentTimestamp = indexCalculator.getCurrentTimestamp();
if (index == null || indexTimestamp < currentTimestamp) {
return true;
} else {
return false;
}
}
}
Now the test:
#Test
public void test() {
final Index index100 = new Index(100);
final Index index200 = new Index(200);
when(indexCalculator.calculateIndex()).thenReturn(index100);
when(indexCalculator.getCurrentTimestamp()).thenReturn(100L);
assertThat(indexService.getIndex()).isSameAs(index100);
verify(indexCalculator).calculateIndex();
verify(indexCalculator).getCurrentTimestamp();
when(indexCalculator.getCurrentTimestamp()).thenReturn(200L);
when(indexCalculator.calculateIndex()).thenReturn(index200);
assertThat(indexService.getIndex()).isSameAs(index100);
verify(indexCalculator, times(2)).getCurrentTimestamp();
// I'd like to see indexCalculator.calculateIndex() called after
// indexService.getIndex() returns the old value but it does not happen
// verify(indexCalculator, times(2)).calculateIndex();
assertThat(indexService.getIndex()).isSameAs(index200);
// Instead, indexCalculator.calculateIndex() os called on
// the next call to indexService.getIndex()
// I'd like to have it earlier
verify(indexCalculator, times(2)).calculateIndex();
verify(indexCalculator, times(3)).getCurrentTimestamp();
verifyNoMoreInteractions(indexCalculator);
}
I'd like to have the value refreshed shortly after it was evicted from the cache. At the moment it is refreshed on the next call of getIndex() first. If the value would have been refreshed right after eviction, this would save me 1s later on.
I've tried #CachePut, but it also does not get me the desired effect. The value is refreshed, but the method is always executed, no matter what condition or unless are.
The only way I see at the moment is to call getIndex() twice(second time async/non-blocking). But that's kind of stupid.
I would say the easiest way of doing what you need is to create a custom Aspect which will do all the magic transparently and which can be reused in more places.
So assuming you have spring-aop and aspectj dependencies on your class path the following aspect will do the trick.
#Aspect
#Component
public class IndexEvictorAspect {
#Autowired
private Cache cache;
#Autowired
private IndexService indexService;
private final ReentrantLock lock = new ReentrantLock();
#AfterReturning(pointcut="hello.IndexService.getIndex()", returning="index")
public void afterGetIndex(Object index) {
if(indexService.isObsolete((Index) index) && lock.tryLock()){
try {
Index newIndex = indexService.calculateIndex();
cache.put(SimpleKey.EMPTY, newIndex);
} finally {
lock.unlock();
}
}
}
}
Several things to note
As your getIndex() method does not have a parameters it is stored in the cache for key SimpleKey.EMPTY
The code assumes that IndexService is in the hello package.
Something like the following could refresh the cache in the desired way and keep the implementation simple and straightforward.
There is nothing wrong about writing clear and simple code, provided it satisfies the requirements.
#Service
public static class IndexService {
#Autowired
private IndexCalculator indexCalculator;
public Index getIndex() {
Index cachedIndex = getCachedIndex();
if (isObsolete(cachedIndex)) {
evictCache();
asyncRefreshCache();
}
return cachedIndex;
}
#Cacheable(cacheNames = "index")
public Index getCachedIndex() {
return indexCalculator.calculateIndex();
}
public void asyncRefreshCache() {
CompletableFuture.runAsync(this::getCachedIndex);
}
#CacheEvict(cacheNames = "index")
public void evictCache() { }
public boolean isObsolete(Index index) {
long indexTimestamp = index.getTimestamp();
long currentTimestamp = indexCalculator.getCurrentTimestamp();
if (index == null || indexTimestamp < currentTimestamp) {
return true;
} else {
return false;
}
}
}
EDIT1:
The caching abstraction based on #Cacheable and #CacheEvict will not work in this case. Those behaviour is following: during #Cacheable call if the value is in cache - return value from the cache, otherwise compute and put into cache and then return; during #CacheEvict the value is removed from the cache, so from this moment there is no value in cache, and thus the first incoming call on #Cacheable will force the recalculation and putting into cache. The use #CacheEvict(condition="") will only do the check on condition wether to remove from cache value during this call based on this condition. So after each invalidation the #Cacheable method will run this heavyweight routine to populate cache.
to have the value beign stored in the cache manager, and updated asynchronously, I would propose to reuse following routine:
#Inject
#Qualifier("my-configured-caching")
private Cache cache;
private ReentrantLock lock = new ReentrantLock();
public Index getIndex() {
synchronized (this) {
Index storedCache = cache.get("singleKey_Or_AnythingYouWant", Index.class);
if (storedCache == null ) {
this.lock.lock();
storedCache = indexCalculator.calculateIndex();
this.cache.put("singleKey_Or_AnythingYouWant", storedCache);
this.lock.unlock();
}
}
if (isObsolete(storedCache)) {
if (!lock.isLocked()) {
lock.lock();
this.asyncUpgrade()
}
}
return storedCache;
}
The first construction is sycnhronized, just to block all the upcoming calls to wait until the first call populates cache.
then the system checks wether the cache should be regenerated. if yes, single call for asynchronous update of the value is called, and the current thread is returning the cached value. upcoming call once the cache is in state of recalculation will simply return the most recent value from the cache. and so on.
with solution like this you will be able to reuse huge volumes of memory, of lets say hazelcast cache manager, as well as multiple key-based cache storage and keep your complex logic of cache actualization and eviction.
OR IF you like the #Cacheable annotations, you can do this following way:
#Cacheable(cacheNames = "index", sync = true)
public Index getCachedIndex() {
return new Index();
}
#CachePut(cacheNames = "index")
public Index putIntoCache() {
return new Index();
}
public Index getIndex() {
Index latestIndex = getCachedIndex();
if (isObsolete(latestIndex)) {
recalculateCache();
}
return latestIndex;
}
private ReentrantLock lock = new ReentrantLock();
#Async
public void recalculateCache() {
if (!lock.isLocked()) {
lock.lock();
putIntoCache();
lock.unlock();
}
}
Which is almost the same, as above, but reuses spring's Caching annotation abstraction.
ORIGINAL:
Why you are trying to resolve this via caching? If this is simple value (not key-based, you can organize your code in simpler manner, keeping in mind that spring service is singleton by default)
Something like that:
#Service
public static class IndexService {
#Autowired
private IndexCalculator indexCalculator;
private Index storedCache;
private ReentrantLock lock = new ReentrantLock();
public Index getIndex() {
if (storedCache == null ) {
synchronized (this) {
this.lock.lock();
Index result = indexCalculator.calculateIndex();
this.storedCache = result;
this.lock.unlock();
}
}
if (isObsolete()) {
if (!lock.isLocked()) {
lock.lock();
this.asyncUpgrade()
}
}
return storedCache;
}
#Async
public void asyncUpgrade() {
Index result = indexCalculator.calculateIndex();
synchronized (this) {
this.storedCache = result;
}
this.lock.unlock();
}
public boolean isObsolete() {
long currentTimestamp = indexCalculator.getCurrentTimestamp();
if (storedCache == null || storedCache.getTimestamp() < currentTimestamp) {
return true;
} else {
return false;
}
}
}
i.e. first call is synchronized and you have to wait until the results are populated. Then if stored value is obsolete the system will perform asynchronous update of the value, but the current thread will receive the stored "cached" value.
I had also introduced the reentrant lock to restrict single upgrade of stored index at time.
I would use a Guava LoadingCache in your index service, like shown in the code sample below:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return getGraphFromDatabase(key);
}
public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {
if (neverNeedsRefresh(key)) {
return Futures.immediateFuture(prevGraph);
} else {
// asynchronous!
ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {
public Graph call() {
return getGraphFromDatabase(key);
}
});
executor.execute(task);
return task;
}
}
});
You can create an async reloading cache loader by calling Guava's method:
public abstract class CacheLoader<K, V> {
...
public static <K, V> CacheLoader<K, V> asyncReloading(
final CacheLoader<K, V> loader, final Executor executor) {
...
}
}
The trick is to run the reload operation in a separate thread, using a ThreadPoolExecutor for example:
On first call, the cache is populated by the load() method, thus it may take some time to answer,
On subsequent calls, when the value needs to be refreshed, it's being computed asynchronously while still serving the stale value. It will serve the updated value once the refresh has completed.
I think it can be something like
#Autowired
IndexService indexService; // self injection
#Cacheable(cacheNames = INDEX_CACHE_NAME)
#CacheEvict(cacheNames = INDEX_CACHE_NAME, condition = "target.isObsolete(#result) && #indexService.calculateIndexAsync()")
public Index getIndex() {
return calculateIndex();
}
public boolean calculateIndexAsync() {
someAsyncService.run(new Runable() {
public void run() {
indexService.updateIndex(); // require self reference to use Spring caching proxy
}
});
return true;
}
#CachePut(cacheNames = INDEX_CACHE_NAME)
public Index updateIndex() {
return calculateIndex();
}
Above code has a problem, if you call to getIndex() again while it is being updated, it will be calculated again. To prevent this, it better to don't use #CacheEvict and let the #Cacheable return the obsolete value until the index has done calculated.
#Autowired
IndexService indexService; // self injection
#Cacheable(cacheNames = INDEX_CACHE_NAME, condition = "!(target.isObsolete(#result) && #indexService.calculateIndexAsync())")
public Index getIndex() {
return calculateIndex();
}
public boolean calculateIndexAsync() {
if (!someThreadSafeService.isIndexBeingUpdated()) {
someAsyncService.run(new Runable() {
public void run() {
indexService.updateIndex(); // require self reference to use Spring caching proxy
}
});
}
return false;
}
#CachePut(cacheNames = INDEX_CACHE_NAME)
public Index updateIndex() {
return calculateIndex();
}
Recently, I came across a situation when creating a factory producing reusable instances.
public class Factory {
private static final int REUSABLE_TIMES_LIMIT = 10;
private static Product instance = new Product();
private static int getTimes;
public static synchronized Product getInstance() {
if (++getTimes >= REUSABLE_TIMES_LIMIT) {
return nextInstance();
}
return instance;
}
public static synchronized Product nextInstance() {
getTimes = 0;
instance = new Product();
return instance;
}
}
Since getInstance() and nextInstance() might both be invoked concurrently by different threads in my case, I choose to add synchronized key words before each of them. However, synchronized is too heavy when lots of threads comes to the method, so I'd like to rewrite this class based on CAS, i.e. those classes in the package of java.util.concurrent.atomic. Unfortunately, I didn't figure out a proper way to arrange my code with two atomic variables, namely instance and getTimes, in the same time. Will someone show me how to correctly use CAS instead of synchronized without causing race condition in this situation? Thanks in advance :)
The one possible option is to use one AtomicReference instead of two. This will make your state consistent regardless of the code compexity.
public static class ProductStorage {
private Product product;
private int getTimes;
public ProductStorage(Product product, int getTimes) {
this.product = product;
this.getTimes = getTimes;
}
}
public static class Factory {
private static final int REUSABLE_TIMES_LIMIT = 10;
private static AtomicReference<ProductStorage> instance = new AtomicReference<>(
new ProductStorage(new Product(), 0)
);
public static Product getInstance() {
ProductStorage current;
for(;;) {
current = instance.get();
if(current.getTimes >= REUSABLE_TIMES_LIMIT) {
instance.compareAndSet(current, new ProductStorage(new Product(), 0));
continue;
}
if(current.getTimes < REUSABLE_TIMES_LIMIT) {
if(instance.compareAndSet(current, new ProductStorage(current.product, current.getTimes + 1))) {
return current.product;
}
}
}
}
}
The first thing you may mention is that new object is always allocated in that case. But remember that most of lock-free algorithms do that and it's not a problem. Allocation in java is fast and costs a few nanoseconds. You may also see similar solution in Martin Thompson's blog. The code is here. On my machine lock-free solution runs 3-4 times fastrer.
If may want to go with two atomics, but that will make counting of getTimes hard.
While creating my app. architecture I faced the need for one structure, that will be described below.
I'm pretty sure, that there is a well known design pattern with the same functionality, because I think that problem, for which I develop it is really common.
I write my own implementation of this, but I always try to use "build in language" implementations of patterns, so - please help me to name this construction.
The idea is close to reader-writer pattern. We have a "container" in which we can add Objects by the key (). And also we can get this objects by keys, removing it from container.
So, the implemented class should have two methods:
void putObject(Key key, Object object);
Object getObject(Key key); // remove <Key,Object> from container.
The next is most interesting.
This container should work in multi-threading environment as follows:
If there is no object associated with key, while calling get(Key
key) method the caller thread should WAIT for the object in this
container.
When another thread will call putObject(Key key, Object object)
method it should check if there is some thread that wait exactly for
this object, and if it is - then signal and wake up the thread that
waits.
I think that it is common structure, does it have "official" name?
My Java implementation of this pattern:
private static interface BlackBox {
public void addObject(IdObject object);
public IdObject getObject(ObjectId id);
}
private static class BlackBoxImpl implements BlackBox {
private final Lock conditionLock = new ReentrantLock();
private final Map<ObjectId, IdObject> savedObjects;
private final Map<ObjectId, Condition> waitingConditions;
public BlackBoxImpl() {
this.savedObjects = new ConcurrentHashMap<ObjectId, IdObject>(20);
this.waitingConditions = new ConcurrentHashMap<ObjectId, Condition>(20);
}
#Override
public void addObject(IdObject object) {
savedObjects.put(object.getId(), object);
if (waitingConditions.containsKey(object.getId())) {
Condition waitCondition = waitingConditions.get(object.getId());
conditionLock.lock();
waitCondition.signal();
conditionLock.unlock();
}
}
#Override
public IdObject getObject(ObjectId id) {
if (savedObjects.containsKey(id)) {
return savedObjects.get(id);
} else {
conditionLock.lock();
Condition waitCondition = conditionLock.newCondition();
waitingConditions.put(id, waitCondition);
waitCondition.awaitUninterruptibly();
conditionLock.unlock();
return savedObjects.get(id);
}
}
}
private static interface IdObject {
public ObjectId getId();
}
private static class IdObjectImpl implements IdObject {
protected final ObjectId id;
public IdObjectImpl(ObjectId id) {
this.id = id;
}
#Override
public ObjectId getId() {
return id;
}
}
private static interface ObjectId {
}
private static class ObjectIdImpl implements ObjectId {
}
I would probably use something like a
ConcurrentMap<K,BlockingQue<V>>.
Use the concurrent methods of the Map to add the pair. Take from your queue for the value. Use an ArrayBlockingQue(1).
Something like this perhaps:
static class MultiQueue<K, V> {
// The base structure.
final ConcurrentMap<K, BlockingQueue<V>> queues = new ConcurrentHashMap<>();
/**
* Put an item in the structure.
*
* The entry in the map will be created if no entry is currently there.
*
* The value will then be posted to the queue.
*/
public void put(K k, V v) throws InterruptedException {
// Make it if not present.
ensurePresence(k).put(v);
}
/**
* Get an item from the structure.
*
* The entry in the map will be created if no entry is currently there.
*
* The value will then be taken from the queue.
*/
public void get(K k) throws InterruptedException {
// Make it if not present - and wait for it.
ensurePresence(k).take();
}
private BlockingQueue<V> ensurePresence(K k) {
// Make it if not present.
return queues.computeIfAbsent(k, v -> new ArrayBlockingQueue(1));
}
}
Looking at your design, to me what you are describing
We have a "container" in which we can add Objects by the key (). And also we can get this objects by keys, removing it from container.
This container should work in multi-threading environment
is close to concurrent Object pool. It uses a set of initialized objects kept ready to use. A client of the pool will request an object from the pool and perform operations on the returned object.
The only real difference I see is that you are getting the objects based on your own criteria.
I'm looking for a collection that:
is a Deque/List - i.e. supports inserting elements at "the top" (newest items go to the top) - deque.addFirst(..) / list.add(0, ..). It could be a Queue, but the iteration order should be reverse - i.e. the most recently added items should come first.
is bounded - i.e. has a limit of 20 items
auto-discards the oldest items (those "at the bottom", added first) when the capacity is reached
non-blocking - if the deque is empty, retrievals should not block. It should also not block / return false / null / throw exception is the deque is full.
concurrent - multiple threads should be able to operate on it
I can take LinkedBlockingDeque and wrap it into my custom collection that, on add operations checks the size and discards the last item(s). Is there a better option?
I made this simple imeplementation:
public class AutoDiscardingDeque<E> extends LinkedBlockingDeque<E> {
public AutoDiscardingDeque() {
super();
}
public AutoDiscardingDeque(int capacity) {
super(capacity);
}
#Override
public synchronized boolean offerFirst(E e) {
if (remainingCapacity() == 0) {
removeLast();
}
super.offerFirst(e);
return true;
}
}
For my needs this suffices, but it should be well-documented methods different than addFirst / offerFirst are still following the semantics of a blocking deque.
I believe what you're looking for is a bounded stack. There isn't a core library class that does this, so I think the best way of doing this is to take a non-synchronized stack (LinkedList) and wrap it in a synchronized collection that does the auto-discard and returning null on empty pop. Something like this:
import java.util.Iterator;
import java.util.LinkedList;
public class BoundedStack<T> implements Iterable<T> {
private final LinkedList<T> ll = new LinkedList<T>();
private final int bound;
public BoundedStack(int bound) {
this.bound = bound;
}
public synchronized void push(T item) {
ll.push(item);
if (ll.size() > bound) {
ll.removeLast();
}
}
public synchronized T pop() {
return ll.poll();
}
public synchronized Iterator<T> iterator() {
return ll.iterator();
}
}
...adding methods like isEmpty as required, if you want it to implement eg List.
The simplest and classic solution is a bounded ring buffer that overrides the oldest elements.
The implementation is rather easy. You need one AtomicInteger/Long for index + AtomicReferenceArray and you have a lock free general purpose stack with 2 methods only offer/poll, no size(). Most concurrent/lock-free structures have hardships w/ size(). Non-overriding stack can have O(1) but w/ an allocation on put.
Something along the lines of:
package bestsss.util;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class ConcurrentArrayStack<E> extends AtomicReferenceArray<E>{
//easy to extend and avoid indirections,
//feel free to contain the ConcurrentArrayStack if feel purist
final AtomicLong index = new AtomicLong(-1);
public ConcurrentArrayStack(int length) {
super(length); //returns
}
/**
* #param e the element to offer into the stack
* #return the previously evicted element
*/
public E offer(E e){
for (;;){
long i = index.get();
//get the result, CAS expect before the claim
int idx = idx(i+1);
E result = get(idx);
if (!index.compareAndSet(i, i+1))//claim index spot
continue;
if (compareAndSet(idx, result, e)){
return result;
}
}
}
private int idx(long idx){//can/should use golden ratio to spread the index around and reduce false sharing
return (int)(idx%length());
}
public E poll(){
for (;;){
long i = index.get();
if (i==-1)
return null;
int idx = idx(i);
E result = get(idx);//get before the claim
if (!index.compareAndSet(i, i-1))//claim index spot
continue;
if (compareAndSet(idx, result, null)){
return result;
}
}
}
}
Last note:
having mod operation is an expensive one and power-of-2 capacity is to preferred, via &length()-1 (also guards vs long overflow).
Here is an implementation that handles concurrency and never returns Null.
import com.google.common.base.Optional;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.locks.ReentrantLock;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class BoundedStack<T> {
private final Deque<T> list = new ConcurrentLinkedDeque<>();
private final int maxEntries;
private final ReentrantLock lock = new ReentrantLock();
public BoundedStack(final int maxEntries) {
checkArgument(maxEntries > 0, "maxEntries must be greater than zero");
this.maxEntries = maxEntries;
}
public void push(final T item) {
checkNotNull(item, "item must not be null");
lock.lock();
try {
list.push(item);
if (list.size() > maxEntries) {
list.removeLast();
}
} finally {
lock.unlock();
}
}
public Optional<T> pop() {
lock.lock();
try {
return Optional.ofNullable(list.poll());
} finally {
lock.unlock();
}
}
public Optional<T> peek() {
return Optional.fromNullable(list.peekFirst());
}
public boolean empty() {
return list.isEmpty();
}
}
For the solution #remery gave, could you not run into a race condition where after if (list.size() > maxEntries) you could erroneously remove the last element if another thread runs pop() in that time period and the list is now within capacity. Given there is no thread synchronization across pop() and public void push(final T item).
For the solution #Bozho gave I would think a similar scenario could be possible? The synchronization is happening on the AutoDiscardingDeque and not with the ReentrantLock inside LinkedBlockingDeque so after running remainingCapacity() another thread could remove some objects from the list and the removeLast() would remove an extra object?