0

Is there a smarter way than ?

....
synchronized (myMap) {
            if (myMap.size() < 5) {
                myMap.putIfAbsent("something", true);
            }
        }
...

UPDATE:

After trying several things I think I might have come up with something better using an AtomicInteger dictating if we can add to the map

if (count.accumulateAndGet(1, (left, right) -> left >= 6 ? 6 : left + right ) <= 5) {
            myMap.putIfAbsent("something", true);
        }
Alexis
  • 1,825
  • 4
  • 23
  • 28

2 Answers2

0

Not only is there not a smart way to do that, but the way you've shown won't work either. synchronized does not lock a ConcurrentHashMap, which uses an entirely different mechanism for its internal concurrency; you still have the race between the size call and put.

You might consider a caching library like Caffeine, which might address your needs here.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 1
    A really good answer would explain why it doesn't work. – Stewart Mar 14 '23 at 13:28
  • Sorry but I still don't understand why there is a race between size and put ? The block is synchronized so thread are going to wait there no? Or are you saying the synchronized doesn't work on `ConcurrentMap` objects in general ? – Alexis Mar 14 '23 at 23:01
  • `synchronized` will not protect from any other operations on the `ConcurrentMap` running at the same time. – Louis Wasserman Mar 15 '23 at 13:41
  • Understood, so.. just by using core java, how can we avoid adding something to the map if the size > 5 ? It's not possible ? Would `synchronized(myObject) {...}` work ? `myObject` simply being `Object myObject = new Object()}` ? I have managed to reproduce the race condition synchronizing on `myMap` but not using a simple object – Alexis Mar 17 '23 at 02:38
  • It's not actually possible to do this correctly and safely with a ConcurrentMap in the presence of other access or modification. A Collections.synchronizedMap can do it, but will be slow as usual. – Louis Wasserman Mar 17 '23 at 05:32
0

One way can be to use the in-built computeIfAbsent(Key, Function) function and check for specific key being present and use the Function part of computeIfAbsent() to calculate value for key.

You can add condition to check for size of ConcurrentHashMap.

    int initialCapacity = 1;
    ConcurrentHashMap<String,String> a=new ConcurrentHashMap<>();
    a.computeIfAbsent("b",x->{return a.size()<=initialCapacity?"800":null;});
    System.out.println(a);

For reference, check this link

Aymendps
  • 1,346
  • 4
  • 20
  • use the .size the computeIfAbsent ? is that really thread safe? – Alexis Mar 17 '23 at 02:32
  • Yes, .size might not completely solve the issue being thread unsafe, something like this might be used for iterating through the ConcurrentHashMap https://stackoverflow.com/a/3768661/21344674 – Shivangi Pandey Mar 17 '23 at 19:14