I was trying to get the maximum value of a calculatedValue in a cycle and I wanted it to be thread safe. So I decided to use AtomicInteger and Math.max, but I can't find a solution so that the operation can be considered atomic.
AtomicInteger value = new AtomicInteger(0);
// Having some cycle here... {
Integer anotherCalculatedValue = ...;
value.set(Math.max(value.get(), anotherCalculatedValue));
}
return value.get()
The problem with that is that I make two operations, therefore is not threadsafe. How can I solve this? The only way is to use synchronized?
If Java 8 is available you can use:
AtomicInteger value = new AtomicInteger(0);
Integer anotherCalculatedValue = ...;
value.getAndAccumulate(anotherCalculatedValue, Math::max);
Which from the specification will:
Atomically updates the current value with the results of
applying the given function to the current and given values,
returning the previous value.
Related
I have a block of code provided below:
Map<String, BigDecimal> salesMap = new HashMap<>();
orderItems.parallelStream().forEach(orderItem -> {
synchronized (this) {
int itemId = orderItem.getItemId();
Item item = settingsClient.getItemByItemId(itemId);
String revenueCenterName = itemIdAndRevenueCenterNameMap.get(itemId);
updateSalesMap(salesMap, "Gross Sales: " + revenueCenterName, orderItem.getNetSales().toPlainString());
}
});
private void updateSalesMap(Map<String,BigDecimal> salesMap, String key, String amount) {
BigDecimal bd = getSalesAmount(salesMap, key);
int scale = 2;
if (StringUtils.isBlank(amount)) {
amount = "0.00";
}
BigDecimal addMe = BigDecimal.valueOf(Double.valueOf(amount)).setScale(scale, RoundingMode.HALF_UP);
salesMap.put(key, bd.add(addMe));
}
The code works fine, but if I don't use the synchronized block, it will end of varying data in the map. As far I know, the streams are thread safe, so I get curious about whats happening. I tried to use ConcurrentHashMap but it seems nothing changed.
My idea is the map data is not written in the RAM and read/ write is done in the thread cache and hence, we end up having various data.
Is it correct? If so, I will use volatile keyword then using a synchronized block.
Note: just find that I cant declare a variable volatile inside a method.
As far I know, the streams are thread safe, so I get curious about whats happening.
They are. As long as you only operate on the stream itself. The problem is that you try to manipulate other variable at the same time (map in this case). The idea of streams is that operations on each of elements are totally independent - check idea of funcional programming.
I tried to use ConcurrentHashMap but it seems nothing changed.
The issue comes from your approach. The general idea is that atomic operations on ConcurrentHashMap are thread safe. However, if you perform two thread safe operations together, it won't be atomic and thread safe. You need to synchronize it yourself or come up with some other solution.
In updateSalesMap() method you first get value from the map, do some calculations and then update the value. This sequence of operations isn't atomic - performing them on ConcurrentHashMap won't change much.
One of possible ways to achieve concurrency in this case would be to utilize CuncurrentHashMap.compute() Javadocs
You are doing read operation using getSalesAmount(salesMap, key) and write operation using salesMap.put(key, bd.add(addMe)), in separate statements. The non-atomicity of this breakup of these operations is not going to change, irrespective of the kind of Map, you use. The synchronized block will solve this ofcourse.
Alternatively, You can use ConcurrentHashMap's compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction), for the kind of atomicity, you are looking for.
I make the updateSalesMap thread-safe and that works for me:
protected synchronized void updateSalesMap(Map<String, BigDecimal> salesMap, String s, String amount) {
BigDecimal bd = updateSalesAmount(salesMap, s);
int scale = 2;
if (StringUtils.isBlank(amount)) {
amount = "0.00";
}
BigDecimal addMe = BigDecimal.valueOf(Double.valueOf(amount)).setScale(scale, RoundingMode.HALF_UP);
salesMap.put(s, bd.add(addMe));
}
The following code works without race condition
AtomicInteger atomicInt = new AtomicInteger(0);
ExecutorService executor = Executors.newFixedThreadPool(20);
IntStream.range(0, 1000)
.forEach(i -> executor.submit(atomicInt::incrementAndGet));
Here is the implementation of incrementAndGet
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
We can see current is not synchronized or locked, after one thread get the current another thread might already update the current.
But it seems like atomic class avoids race condition some how.
Can someone point out my mistake?
compareAndSet sets the value (and returns true) if and only if the first parameter is equal to the AtomicInteger's current value.
That is, if another thread had already changed the value, then current would not be equal to the current value, and the loop would run once more.
From the documentation of compareAndSet(int expect, int update):
Atomically sets the value to the given updated value if the current
value == the expected value.
Can someone tell if LongAccumulator could be a better alternative for AtomicInteger in the below example?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class IncrementThread implements Runnable{
AtomicInteger atomicint = new AtomicInteger();
public IncrementThread(AtomicInteger atominint) {
this.atomicint = atominint;
}
#Override
public void run() {
while(true)
if(atomicint.incrementAndGet()==4){doSomething(); atomicint.set(0);}
}
private void doSomething() {
System.out.println(Thread.currentThread().getName() + " : counter reached to 4");
}
public static void main(String[] args) {
AtomicInteger atomicint = new AtomicInteger();
IncrementThread incThread1 = new IncrementThread(atomicint);
IncrementThread incThread2 = new IncrementThread(atomicint);
IncrementThread incThread3 = new IncrementThread(atomicint);
ExecutorService threadPool = Executors.newCachedThreadPool();
threadPool.execute(incThread1);
threadPool.execute(incThread2);
threadPool.execute(incThread3);
}
}
In this very exact example (which really does nothing useful) both classes seem to be equivalent.
With LongAccumulator you could pass the whole if statement as a lambda expression. But: The java doc of LongAccumulator states that the supplied function should be side effect free which is not fulfilled (doSomething writes to system out).
I suppose that you would probably use LongAccumulator in the same manner as AtomicInteger in this example. In that case the answer is no.
LongAccumulator accumulate values and count the result only, when you call methods like get(). You also make clearing similar LongAccumulator.reset() method, when it would be has 4 value. But all of this methods in LongAccumulator are not thread safe and you could get unpredictable results, because you use multiple threads for reading and updating.
LongAccumulator is good, when you know that many different threads will be update value, but the reading is seldom and more over, you should be sure that reading or reset happen only in one thread, if you matter about synchronization.
But if you don't, LongAccumulator could be better. For example, when you want to count statistic, because if you want get statistic you more probably don't mean "statistic exactly in time of calling", you mean something like "current" results.
This reasoning is applicable for LongAdder too.
In your example there are two possible improvements.
First, you can use:
while(true)
if(atomicint.incrementAndGet()==4){doSomething(); atomicint.compareAndSet(4, 0);}
Now you check, that you reset the same state of atomicint variable.
Another possible improvement - don't use AtomicLong or LongAccumulator, just use simple long variable with volatile keyword. It will be simpler and more applicable here, because in this example you don't use capabilities (like I mention in first improvement).
You could know more in documentation and classes' sources
LongAdder
LongAccumulator
About volatile
Most efficient - sources :)
Would this operation be atomic or is there a chance of data race in between?
atomicInteger.set(-atomicInteger.get());
If there is a data race, how to negate an AtomicInteger atomically?
I would do it this way
public int getAndNegate(AtomicInteger i) {
for (;;) {
int current = i.get();
int next = -current;
if (i.compareAndSet(current, next))
return current;
}
}
No you need to synchronize to lock the instance I guess.
AtomicInteger has lots of methods to getAndSet but nothing to do inverse...
Apparently this was asked before on SO Does AtomicBoolean not have a negate() method? The solution on that page is interesting.
public final int getAndDecrement()
Atomically decrements by one the current value. Returns the previous value
AtomicInteger itsCounter = new AtomicInteger();
itsCounter.getAndDecrement();
I would like to collect some metrics from various places in a web app. To keep it simple, all these will be counters and therefore the only modifier operation is to increment them by 1.
The increments will be concurrent and often. The reads (dumping the stats) is a rare operation.
I was thinking to use a ConcurrentHashMap. The issue is how to increment the counters correctly. Since the map doesn't have an "increment" operation, I need to read the current value first, increment it than put the new value in the map. Without more code, this is not an atomic operation.
Is it possible to achieve this without synchronization (which would defeat the purpose of the ConcurrentHashMap)? Do I need to look at Guava ?
Thanks for any pointers.
P.S.
There is a related question on SO (Most efficient way to increment a Map value in Java) but focused on performance and not multi-threading
UPDATE
For those arriving here through searches on the same topic: besides the answers below, there's a useful presentation which incidentally covers the same topic. See slides 24-33.
In Java 8:
ConcurrentHashMap<String, LongAdder> map = new ConcurrentHashMap<>();
map.computeIfAbsent("key", k -> new LongAdder()).increment();
Guava's new AtomicLongMap (in release 11) might address this need.
You're pretty close. Why don't you try something like a ConcurrentHashMap<Key, AtomicLong>?
If your Keys (metrics) are unchanging, you could even just use a standard HashMap (they are threadsafe if readonly, but you'd be well advised to make this explicit with an ImmutableMap from Google Collections or Collections.unmodifiableMap, etc.).
This way, you can use map.get(myKey).incrementAndGet() to bump statistics.
Other than going with AtomicLong, you can do the usual cas-loop thing:
private final ConcurrentMap<Key,Long> counts =
new ConcurrentHashMap<Key,Long>();
public void increment(Key key) {
if (counts.putIfAbsent(key, 1)) == null) {
return;
}
Long old;
do {
old = counts.get(key);
} while (!counts.replace(key, old, old+1)); // Assumes no removal.
}
(I've not written a do-while loop for ages.)
For small values the Long will probably be "cached". For longer values, it may require allocation. But the allocations are actually extremely fast (and you can cache further) - depends upon what you expect, in the worst case.
Got a necessity to do the same.
I'm using ConcurrentHashMap + AtomicInteger.
Also, ReentrantRW Lock was introduced for atomic flush(very similar behavior).
Tested with 10 Keys and 10 Threads per each Key. Nothing was lost.
I just haven't tried several flushing threads yet, but hope it will work.
Massive singleusermode flush is torturing me...
I want to remove RWLock and break down flushing into small pieces. Tomorrow.
private ConcurrentHashMap<String,AtomicInteger> counters = new ConcurrentHashMap<String, AtomicInteger>();
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void count(String invoker) {
rwLock.readLock().lock();
try{
AtomicInteger currentValue = counters.get(invoker);
// if entry is absent - initialize it. If other thread has added value before - we will yield and not replace existing value
if(currentValue == null){
// value we want to init with
AtomicInteger newValue = new AtomicInteger(0);
// try to put and get old
AtomicInteger oldValue = counters.putIfAbsent(invoker, newValue);
// if old value not null - our insertion failed, lets use old value as it's in the map
// if old value is null - our value was inserted - lets use it
currentValue = oldValue != null ? oldValue : newValue;
}
// counter +1
currentValue.incrementAndGet();
}finally {
rwLock.readLock().unlock();
}
}
/**
* #return Map with counting results
*/
public Map<String, Integer> getCount() {
// stop all updates (readlocks)
rwLock.writeLock().lock();
try{
HashMap<String, Integer> resultMap = new HashMap<String, Integer>();
// read all Integers to a new map
for(Map.Entry<String,AtomicInteger> entry: counters.entrySet()){
resultMap.put(entry.getKey(), entry.getValue().intValue());
}
// reset ConcurrentMap
counters.clear();
return resultMap;
}finally {
rwLock.writeLock().unlock();
}
}
I did a benchmark to compare the performance of LongAdder and AtomicLong.
LongAdder had a better performance in my benchmark: for 500 iterations using a map with size 100 (10 concurrent threads), the average time for LongAdder was 1270ms while that for AtomicLong was 1315ms.