Two threads accessing the same ArrayList at the same time? - java

I have the following code in thread 1:
synchronized (queues.get(currentQueue)) { //line 1
queues.get(currentQueue).add(networkEvent); //line 2
}
and the following in thread 2:
synchronized (queues.get(currentQueue)) {
if (queues.get(currentQueue).size() > 10) {
currentQueue = 1;
}
}
Now to my question: The currentQueue variable currently has the value of 0. When thread 2 changes the value of currentQueue to 1 and thread 1 waits at line 1 (because of the synchronized), does thread 1 then use the updated currentQueue value in line 2 after thread 2 has finished (that's what I want to).

The answer to the question is that it depends. I assume there is other chunk of code that increments the currentQueue variable. This being the case, the lock is happening not at the 'currentQueue' variable and neither is it happening at the collection of 'queues', but rather it is happening on one of the 10 queues (or however many you have) in the 'queues' collection.
Hence, if both threads happen to access the same queue (say queue 5), then the answer to your question is yes. However, for that to happen is one in ten chance (one in x chance, where x = the number or queues in the 'queues' collection). Therefore, if the threads access different queues, then the answer is no.

The correct answer to your question is: The result is undefined.
Your monitor object is queues.get(currentQueue), but since currentQueue is variable, your monitor is variable, therefore the state it is currently in is more or less random. Effectively this code would break eventually.
A simple way to fix it would be a function like this:
protected synchronized QueueType getCurrentQueue() {
return queues.get(currentQueue);
}
However this is still a bad way of implementing the whole thing. You should either try to eliminate the synchronization completely through the use of a concurrent Queue (like ConcurrentLinkedQueue) or work with a lock/final monitor object.
final Object queueLock = new Object();
...
synchronized(queueLock) {
queues.get(currentQueue).add(networkEvent);
}
Note that you will have to use that locking every time you access queues or currentQueue as both define the dataset you are using.

Assuming you have no other thread will change the value of currentQueue, yes Thread 1 will end up using the queue pointed to by the updated value of currentQueue, since you're invoking queues.get(currentQueue) once again in the body of the synchronized block. This however doesn't mean that your synchronization is sound. You actually should synchronize on currentQueue, since it seems to be the shared key to access the current queue.
Also remember when you use synchronize you're synchronizing on the reference of the variable, and not its value. So if you reassign a new object to it, your synchronization doesn't make sense anymore.

Related

how synchronized keyword works internally

I read the below program and answer in a blog.
int x = 0;
boolean bExit = false;
Thread 1 (not synchronized)
x = 1;
bExit = true;
Thread 2 (not synchronized)
if (bExit == true)
System.out.println("x=" + x);
is it possible for Thread 2 to print “x=0”?
Ans : Yes ( reason : Every thread has their own copy of variables. )
how do you fix it?
Ans: By using make both threads synchronized on a common mutex or make both variable volatile.
My doubt is : If we are making the 2 variable as volatile then the 2 threads will share the variables from the main memory. This make a sense, but in case of synchronization how it will be resolved as both the thread have their own copy of variables.
Please help me.
This is actually more complicated than it seems. There are several arcane things at work.
Caching
Saying "Every thread has their own copy of variables" is not exactly correct. Every thread may have their own copy of variables, and they may or may not flush these variables into the shared memory and/or read them from there, so the whole thing is non-deterministic. Moreover, the very term flushing is really implementation-dependent. There are strict terms such as memory consistency, happens-before order, and synchronization order.
Reordering
This one is even more arcane. This
x = 1;
bExit = true;
does not even guarantee that Thread 1 will first write 1 to x and then true to bExit. In fact, it does not even guarantee that any of these will happen at all. The compiler may optimize away some values if they are not used later. The compiler and CPU are also allowed to reorder instructions any way they want, provided that the outcome is indistinguishable from what would happen if everything was really in program order. That is, indistinguishable for the current thread! Nobody cares about other threads until...
Synchronization comes in
Synchronization does not only mean exclusive access to resources. It is also not just about preventing threads from interfering with each other. It's also about memory barriers. It can be roughly described as each synchronization block having invisible instructions at the entry and exit, the first one saying "read everything from the shared memory to be as up-to-date as possible" and the last one saying "now flush whatever you've been doing there to the shared memory". I say "roughly" because, again, the whole thing is an implementation detail. Memory barriers also restrict reordering: actions may still be reordered, but the results that appear in the shared memory after exiting the synchronized block must be identical to what would happen if everything was indeed in program order.
All that only works, of course, only if both blocks use the same locking object.
The whole thing is described in details in Chapter 17 of the JLS. In particular, what's important is the so-called "happens-before order". If you ever see in the documentation that "this happens-before that", it means that everything the first thread does before "this" will be visible to whoever does "that". This may even not require any locking. Concurrent collections are a good example: one thread puts there something, another one reads that, and that magically guarantees that the second thread will see everything the first thread did before putting that object into the collection, even if those actions had nothing to do with the collection itself!
Volatile variables
One last warning: you better give up on the idea that making variables volatile will solve things. In this case maybe making bExit volatile will suffice, but there are so many troubles that using volatiles can lead to that I'm not even willing to go into that. But one thing is for sure: using synchronized has much stronger effect than using volatile, and that goes for memory effects too. What's worse, volatile semantics changed in some Java version so there may exist some versions that still use the old semantics which was even more obscure and confusing, whereas synchronized always worked well provided you understand what it is and how to use it.
Pretty much the only reason to use volatile is performance because synchronized may cause lock contention and other troubles. Read Java Concurrency in Practice to figure all that out.
Q & A
1) You wrote "now flush whatever you've been doing there to the shared
memory" about synchronized blocks. But we will see only the variables
that we access in the synchronize block or all the changes that the
thread call synchronize made (even on the variables not accessed in the
synchronized block)?
Short answer: it will "flush" all variables that were updated during the synchronized block or before entering the synchronized block. And again, because flushing is an implementation detail, you don't even know whether it will actually flush something or do something entirely different (or doesn't do anything at all because the implementation and the specific situation already somehow guarantee that it will work).
Variables that wasn't accessed inside the synchronized block obviously won't change during the execution of the block. However, if you change some of those variables before entering the synchronized block, for example, then you have a happens-before relationship between those changes and whatever happens in the synchronized block (the first bullet in 17.4.5). If some other thread enters another synchronized block using the same lock object then it synchronizes-with the first thread exiting the synchronized block, which means that you have another happens-before relationship here. So in this case the second thread will see the variables that the first thread updated prior to entering the synchronized block.
If the second thread tries to read those variables without synchronizing on the same lock, then it is not guaranteed to see the updates. But then again, it isn't guaranteed to see the updates made inside the synchronized block as well. But this is because of the lack of the memory-read barrier in the second thread, not because the first one didn't "flush" its variables (memory-write barrier).
2) In this chapter you post (of JLS) it is written that: "A write to a
volatile field (§8.3.1.4) happens-before every subsequent read of that
field." Doesn't this mean that when the variable is volatile you will
see only changes of it (because it is written write happens-before
read, not happens-before every operation between them!). I mean
doesn't this mean that in the example, given in the description of the
problem, we can see bExit = true, but x = 0 in the second thread if
only bExit is volatile? I ask, because I find this question here: http://java67.blogspot.bg/2012/09/top-10-tricky-java-interview-questions-answers.html
and it is written that if bExit is volatile the program is OK. So the
registers will flush only bExits value only or bExits and x values?
By the same reasoning as in Q1, if you do bExit = true after x = 1, then there is an in-thread happens-before relationship because of the program order. Now since volatile writes happen-before volatile reads, it is guaranteed that the second thread will see whatever the first thread updated prior to writing true to bExit. Note that this behavior is only since Java 1.5 or so, so older or buggy implementations may or may not support this. I have seen bits in the standard Oracle implementation that use this feature (java.concurrent collections), so you can at least assume that it works there.
3) Why monitor matters when using synchronized blocks about memory
visibility? I mean when try to exit synchronized block aren't all
variables (which we accessed in this block or all variables in the
thread - this is related to the first question) flushed from registers
to main memory or broadcasted to all CPU caches? Why object of
synchronization matters? I just cannot imagine what are relations and
how they are made (between object of synchronization and memory).
I know that we should use the same monitor to see this changes, but I
don't understand how memory that should be visible is mapped to
objects. Sorry, for the long questions, but these are really
interesting questions for me and it is related to the question (I
would post questions exactly for this primer).
Ha, this one is really interesting. I don't know. Probably it flushes anyway, but Java specification is written with high abstraction in mind, so maybe it allows for some really weird hardware where partial flushes or other kinds of memory barriers are possible. Suppose you have a two-CPU machine with 2 cores on each CPU. Each CPU has some local cache for every core and also a common cache. A really smart VM may want to schedule two threads on one CPU and two threads on another one. Each pair of the threads uses its own monitor, and VM detects that variables modified by these two threads are not used in any other threads, so it only flushes them as far as the CPU-local cache.
See also this question about the same issue.
4) I thought that everything before writing a volatile will be up to
date when we read it (moreover when we use volatile a read that in
Java it is memory barrier), but the documentation don't say this.
It does:
17.4.5.
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
If hb(x, y) and hb(y, z), then hb(x, z).
A write to a volatile field (§8.3.1.4) happens-before every subsequent
read of that field.
If x = 1 comes before bExit = true in program order, then we have happens-before between them. If some other thread reads bExit after that, then we have happens-before between write and read. And because of the transitivity, we also have happens-before between x = 1 and read of bExit by the second thread.
5) Also, if we have volatile Person p does we have some dependency
when we use p.age = 20 and print(p.age) or have we memory barrier in
this case(assume age is not volatile) ? - I think - No
You are correct. Since age is not volatile, then there is no memory barrier, and that's one of the trickiest things. Here is a fragment from CopyOnWriteArrayList, for example:
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
Here, getArray and setArray are trivial setter and getter for the array field. But since the code changes elements of the array, it is necessary to write the reference to the array back to where it came from in order for the changes to the elements of the array to become visible. Note that it is done even if the element being replaced is the same element that was there in the first place! It is precisely because some fields of that element may have changed by the calling thread, and it's necessary to propagate these changes to future readers.
6) And is there any happens before 2 subsequent reads of volatile
field? I mean does the second read will see all changes from thread
which reads this field before it(of course we will have changes only
if volatile influence visibility of all changes before it - which I am
a little confused whether it is true or not)?
No, there is no relationship between volatile reads. Of course, if one thread performs a volatile write and then two other thread perform volatile reads, they are guaranteed to see everything at least up to date as it was before the volatile write, but there is no guarantee of whether one thread will see more up-to-date values than the other. Moreover, there is not even strict definition of one volatile read happening before another! It is wrong to think of everything happening on a single global timeline. It is more like parallel universes with independent timelines that sometimes sync their clocks by performing synchronization and exchanging data with memory barriers.
It depends on the implementation which decides if threads will keep a copy of the variables in their own memory. In case of class level variables threads have a shared access and in case of local variables threads will keep a copy of it. I will provide two examples which shows this fact , please have a look at it.
And in your example if I understood it correctly your code should look something like this--
package com.practice.multithreading;
public class LocalStaticVariableInThread {
static int x=0;
static boolean bExit = false;
public static void main(String[] args) {
Thread t1=new Thread(run1);
Thread t2=new Thread(run2);
t1.start();
t2.start();
}
static Runnable run1=()->{
x = 1;
bExit = true;
};
static Runnable run2=()->{
if (bExit == true)
System.out.println("x=" + x);
};
}
Output
x=1
I am getting this output always. It is because the threads share the variable and the when it is changed by one thread other thread can see it. But in real life scenarios we can never say which thread will start first, since here the threads are not doing anything we can see the expected result.
Now take this example--
Here if you make the i variable inside the for-loop` as static variable then threads won t keep a copy of it and you won t see desired outputs, i.e. the count value will not be 2000 every time even if u have synchronized the count increment.
package com.practice.multithreading;
public class RaceCondition2Fixed {
private int count;
int i;
/*making it synchronized forces the thread to acquire an intrinsic lock on the method, and another thread
cannot access it until this lock is released after the method is completed. */
public synchronized void increment() {
count++;
}
public static void main(String[] args) {
RaceCondition2Fixed rc= new RaceCondition2Fixed();
rc.doWork();
}
private void doWork() {
Thread t1 = new Thread(new Runnable() {
#Override
public void run() {
for ( i = 0; i < 1000; i++) {
increment();
}
}
});
Thread t2 = new Thread(new Runnable() {
#Override
public void run() {
for ( i = 0; i < 1000; i++) {
increment();
}
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
/*if we don t use join then count will be 0. Because when we call t1.start() and t2.start()
the threads will start updating count in the spearate threads, meanwhile the main thread will
print the value as 0. So. we need to wait for the threads to complete. */
System.out.println(Thread.currentThread().getName()+" Count is : "+count);
}
}

Multithreads: lock on get and set

I know that in a program that works with multiple threads it's necessary to synchronize the methods because it's possible to have problems like race conditions.
But I cannot understand why we need to synchronize also the methods that need just to read a shared variable.
Look at this example:
public ConcurrentIntegerArray(final int size) {
arr = new int[size];
}
public void set(final int index, final int value) {
lock.lock();
try {
arr[index] = value;
} finally {
lock.unlock();
}
}
public int get(final int index) {
lock.lock();
try {
return arr[index];
} finally {
lock.unlock();
}
}
They did a look on the get and also on the set method. On the set method I understand why. For example if I want to put with Thread1 in index=3 the number 5 and after some milliseconds the Thread2 have to put in index=3 the number 6. Can it happen that I have in index=3 in my array still a 5 instead of a 6 (if I don't do a synchronization on the method set)? This because the Thread1 can have a switch-context and so the Thread2 enter in the same method put the value and after the Thread1 assign the value 5 on the same position So instead of a 6 I have a 5.
But I don't understand why we need (look the example) to synchronize also the method get. I'm asking this question because we need just to read on the memory and not to write.So why we need also on the method get to have a synchronization? Can someone give to me a very simple example?
Both methods need to be synchronized. Without synchronization on the get method, this sequence is possible:
get is called, but the old value isn't returned yet.
Another thread calls set and updates the value.
The first thread that called get now examines the now-returned value and sees what is now an outdated value.
Synchronization would disallow this scenario by guaranteeing that another thread can't just call set and invalidate the get value before it even returns. It would force a thread that calls set to wait for the thread that calls get to finish.
If you do not lock in the get method than a thread might keep a local copy of the array and never refreshes from the main memory. So its possible that a get never sees a value which was updated by a set method. Lock will force the visibility.
Each thread maintain their own copy of value. The synchronized ensures that the coherency is maintained between different threads. Without synchronized, one can never be sure if any one has modified it. Alternatively, one can define the variable as volatile and it will have the same memory effects as synchronized.
The locking action also guarantees memory visibility. From the Lock doc:
All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, [...]:
A successful lock operation has the same memory synchronization effects as a successful Lock action.
A successful unlock operation has the same memory synchronization effects as a successful Unlock action.
Without acquiring the lock, due to memory consistency errors, there's no reason a call to get needs to see the most updated value. Modern processors are very fast, access to DRAM is comparatively very slow, so processors store values they are working on in a local cache. In concurrent programming this means one thread might write to a variable in memory but a subsequent read from a different thread gets a stale value because it is read from the cache.
The locking guarantees that the value is actually read from memory and not from the cache.

How atomicity is achieved in the classes defined in java.util.concurrent.atomic package?

I was going through the source code of java.util.concurrent.atomic.AtomicInteger to find out how atomicity is achieved by the atomic operations provided by the class. For instance AtomicInteger.getAndIncrement() method source is as follows
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
I am not able to understand the purpose of writing the sequence of operations inside a infinite for loop. Does it serve any special purpose in Java Memory Model (JMM). Please help me find a descriptive understanding. Thanks in advance.
I am not able to understand the purpose of writing the sequence of operations inside a infinite for loop.
The purpose of this code is to ensure that the volatile field gets updated appropriately without the overhead of a synchronized lock. Unless there are a large number of threads all competing to update this same field, this will most likely spin a very few times to accomplish this.
The volatile keyword provides visibility and memory synchronization guarantees but does not in itself ensure atomic operations with multiple operations (test and set). If you are testing and then setting a volatile field there are race-conditions if multiple threads are trying to perform the same operation at the same time. In this case, if multiple threads are trying to increment the AtomicInteger at the same time, you might miss one of the increments. The concurrent code here uses the spin loop and the compareAndSet underlying methods to make sure that the volatile int is only updated to 4 (for example) if it still is equal to 3.
t1 gets the atomic-int and it is 0.
t2 gets the atomic-int and it is 0.
t1 adds 1 to it
t1 atomically tests to make sure it is 0, it is, and stores 1.
t2 adds 1 to it
t2 atomically tests to make sure it is 0, it is not, so it has to spin and try again.
t2 gets the atomic-int and it is 1.
t2 adds 1 to it
t2 atomically tests to make sure it is 1, it is, and stores 2.
Does it serve any special purpose in Java Memory Model (JMM).
No, it serves the purpose of the class and method definitions and uses the JMM and the language definitions around volatile to achieve its purpose. The JMM defines what the language does with the synchronized, volatile, and other keywords and how multiple threads interact with cached and central memory. This is mostly about native code interactions with operating system and hardware and is rarely, if ever, about Java code.
It is the compareAndSet(...) method which gets closer to the JMM by calling into the Unsafe class which is mostly native methods with some wrappers:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
I am not able to understand the purpose of writing the sequence of
operations inside a infinite for loop.
To understand why it is in an infinite loop I find it helpful to understand what the compareAndSet does and how it may return false.
Atomically sets the value to the given updated value if the current
value == the expected value.
Parameters:
expect - the expected value
update - the new value
Returns:
true if successful. False return indicates that the actual value was not
equal to the expected value
So you read the Returns message and ask how is that possible?
If two threads are invoking incrementAndGet at close to the same time, and they both enter and see the value current == 1. Both threads will create a thread-local next == 2 and try to set via compareAndSet. Only one thread will win as per documented and the thread that loses must try again.
This is how CAS works. You attempt to change the value if you fail, try again, if you succeed then continue on.
Now simply declaring the field as volatile will not work because incrementing is not atomic. So something like this is not safe from the scenario I explained
volatile int count = 0;
public int incrementAndGet(){
return ++count; //may return the same number more than once.
}
Java's compareAndSet is based on CPU compare-and-swap (CAS) instructions see http://en.wikipedia.org/wiki/Compare-and-swap. It compares the contents of a memory location to a given value and, only if they are the same, modifies the contents of that memory location to a given new value.
In case of incrementAndGet we read the current value and call compareAndSet(current, current + 1). If it returns false it means that another thread interfered and changed the current value, which means that our attempt failed and we need to repeat the whole cycle until it succeeds.

behaviour of volatile keyword in java

I need some example on Volatile Keyword of Java Threads.
As per definition of volatile keyword it says, when variable is declared as volatile then thread will directly read/write to variable memory instead of read/write from local thread cache.
please correct me if I am wrong.
So in that understanding when I run the below program,
public class ThreadRunnableBoth implements Runnable{
private volatile int num =0;
public void run(){
Thread t = Thread.currentThread();
String name = t.getName();
for(int i=0; i<100; i++){
if(name.equals("Thread1")){
num=10;
System.out.println("value of num 1 is :"+num);
}else{
num=15;
System.out.println("value of num 2 is :"+num);
}
}
}
public static void main(String args[]) throws InterruptedException{
Runnable r = new ThreadRunnableBoth();
Thread t1 = new Thread(r);
t1.setName("Thread1");
Thread t2 = new Thread(r);
t2.setName("Thread2");
t1.start();
t2.start();
}
}
I got these example from some site and when i tried running it I cant see any difference removing Volatile or adding Volatile Keyword.
Please explain me the difference happens on removing it and adding it.
Thanks a lot.
The main differences between having a volatile keyword or not is whether you need a memory fence to safely operate with the data.
Memory fences prevent side effects that can occur amongst multiple threads due to out-of-order execution. By instructing the CPU, the compiler / runtime environment can tell the CPU that the original ordering constraint on the read cannot be manipulated without destroying the correctness of the program.
Read up on memory fences here, and remember that the key to the solution is consistency, not location. The read request can stop at cache, provide that the cache is guaranteed to be consistent (by the CPU's internal mechanisms).
As per definition of volatile keyword it says, when variable is
declared as volatile then thread will directly read/write to variable
memory instead of read/write from local thread cache.
Not necessarily. A system that supports cache coherence can have volatile fields up to date without ever reading from main-memory. Volatile says that each thread will see the most up-to-date value of a certain field.
As for memory visibility you won't necessarily see any changes (immediately) if you remove volatile but your program is suspect to failure. The longer it runs the more problems you may end up seeing.
So without the volatile keyword, the threads are just printing the value of num in their local memory cache. Their changes to num are in no way synchronized with the other thread's view of num. I see output like:
value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
...
With volatile, they are both updating and printing to the same global storage location with memory barriers around the set/get. But this won't change the output which is very subject to race conditions. I see output like:
value of num 2 is :15
value of num 1 is :15
value of num 2 is :15
value of num 1 is :10
value of num 2 is :15
value of num 1 is :10
...
There is a race between which set was last when the value is printed.
You may not be seeing this output because your processor architecture or JRE is context switching only on the IO events or otherwise not providing a full threaded execution. If you show some output then I can comment some more.
The effect of volatile variable is evident on multiprocessor system wherein different threads run on different processors. On ordinary single processor system, the impact may not be evident.
Here is good discussion thread on this site on the same subject.
In your example, num starts with a default value of 0, and you then (on its declaration line) assign it to 0. That assignment would be a data race if num weren't volatile, but of course you wouldn't be able to tell the difference.
Then you only use num in one thread, and within a thread you will always see things happen in the order the code said they would. So in this case, num doesn't have to be volatile.
Now, if you modified your main method so that it checked t1.num after the thread had started (but without checking that it has finished in a way that creates a happens-before edge, like Thread.join), you would have a data race without num being volatile. You could have main wait for 5 days, and it still wouldn't be guaranteed to see num as anything other than 0. And not just that, but if ThreadRunnableBoth also had a non-volatile boolean that started false and were set to true at the end of the leap, main could also see that boolean as true (thus meaning the thread had finished) but num still at 0! This is a data race, and can happen (for instance) on a multicore machine where the boolean is flushed out of a local register before num is. In this example, making both num and the boolean volatile will ensure that if the boolean is true, num == 0 || num == 15.
But here's the kicker: even without the volatile keyword -- that is, even in the presence of a data race -- you're not guaranteed to see racy behavior. That is, the data race says you can't guarantee that you'll see the change in another thread -- but it doesn't guarantee that you won't. It could be that it works just fine 100 times on your machine, and then someone puts it on an 8-core machine in the wild, and it's part of a more complex program so it gets optimized differently, and then things break.
Most of the talk is about hardware. Actually compiler optimisations are typically more relevant. You're accessing a field repetitively in a small method, so let's put it in a register. Altering the physical memory wont alter the value in the register.
Although the ("new", but many years old) Java Memory Model (JMM) does not talk about main memory like the old one and does not provide guarantees of progress (very difficult to actually specify), implementation of the volatile/happens-before specification will result in eviction from the register and synchronisation between threads.

How to make updating BigDecimal within ConcurrentHashMap thread safe

I am making an application that takes a bunch of journal entries and calculate sum.
Is below way of doing it is thread/concurrency safe when there are multiple threads calling the addToSum() method. I want to ensure that each call updates the total properly.
If it is not safe, please explain what do I have to do to ensure thread safety.
Do I need to synchronize the get/put or is there a better way?
private ConcurrentHashMap<String, BigDecimal> sumByAccount;
public void addToSum(String account, BigDecimal amount){
BigDecimal newSum = sumByAccount.get(account).add(amount);
sumByAccount.put(account, newSum);
}
Thanks so much!
Update:
Thanks everyone for the answer, I already get that the code above is not thread-safe.
Thanks Vint for suggesting the AtomicReference as an alternative to synchronize. I was using AtomicInteger to hold integer sums before and I was wondering if there are something like that for BigDecimal.
Is the a definitive conclusion on the pro and con of the two?
You can use synchronized like the others suggested but if want a minimally blocking solution you can try AtomicReference as a store for the BigDecimal
ConcurrentHashMap<String,AtomicReference<BigDecimal>> map;
public void addToSum(String account, BigDecimal amount) {
AtomicReference<BigDecimal> newSum = map.get(account);
for (;;) {
BigDecimal oldVal = newSum.get();
if (newSum.compareAndSet(oldVal, oldVal.add(amount)))
return;
}
}
Edit - I'll explain this more:
An AtomicReference uses CAS to atomically assigns a single reference. The loop says this.
If the current field stored in AtomicReference == oldVal [their location in memory, not their value] then replace the value of the field stored in AtomicReference with oldVal.add(amount). Now, any time after the for-loop you invoke newSum.get() it will have the BigDecimal object that has been added to.
You want to use a loop here because it is possible two threads are trying to add to the same AtomicReference. It can happen that one thread succeeds and another thread fails, if that happens just try again with the new added value.
With moderate thread contention this would be a faster implementation, with high contention you are better off using synchronized
Your solution is not thread safe. The reason is that it is possible for a sum to be missed since the operation to put is separate from the operation to get (so the new value you are putting into the map could miss a sum that is being added at the same time).
The safest way to do what you want to do is to synchronize your method.
That is not safe, because threads A and B might both call sumByAccount.get(account) at the same time (more or less), so neither one will see the result of the other's add(amount). That is, things might happen in this sequence:
thread A calls sumByAccount.get("accountX") and gets (for example) 10.0.
thread B calls sumByAccount.get("accountX") and gets the same value that thread A did: 10.0.
thread A sets its newSum to (say) 10.0 + 2.0 = 12.0.
thread B sets its newSum to (say) 10.0 + 5.0 = 15.0.
thread A calls sumByAccount.put("accountX", 12.0).
thread B calls sumByAccount.put("accountX", 15.0), overwriting what thread A did.
One way to fix this is to put synchronized on your addToSum method, or to wrap its contents in synchronized(this) or synchronized(sumByAccount). Another way, since the above sequence of events only happens if two threads are updating the same account at the same time, might be to synchronize externally based on some sort of Account object. Without seeing the rest of your program logic, I can't be sure.
Yes, you need to synchronize since otherwise you can have two threads each getting the same value (for the same key), say A and thread 1 add B to it and thread 2 adds C to it and store it back. The result now will not be A+B+C, but A+B or A+C.
What you need to do is lock on something that is common to the additions. Synchronizing on get/put will not help, unless you do
synchronize {
get
add
put
}
but if you do that then you will prevent threads from updating values even if it is for different keys. You want to synchronize on the account. However, synchronizing on the string seems unsafe as it could lead to deadlocks (you don't know what else locks the string). Can you create an account object instead and use that for locking?

Categories

Resources