Java Synchronization - Mutex.wait vs List.wait - java

While using Java Threading Primitives to construct a thread safe bounded queue - whats the difference between these 2 constructs
Creating an explicit lock object.
Using the list as the lock and waiting on it.
Example of 1
private final Object lock = new Object();
private ArrayList<String> list = new ArrayList<String>();
public String dequeue() {
synchronized (lock) {
while (list.size() == 0) {
lock.wait();
}
String value = list.remove(0);
lock.notifyAll();
return value;
}
}
public void enqueue(String value) {
synchronized (lock) {
while (list.size() == maxSize) {
lock.wait();
}
list.add(value);
lock.notifyAll();
}
}
Example of 2
private ArrayList<String> list = new ArrayList<String>();
public String dequeue() {
synchronized (list) { // lock on list
while (list.size() == 0) {
list.wait(); // wait on list
}
String value = list.remove(0);
list.notifyAll();
return value;
}
}
public void enqueue(String value) {
synchronized (list) { // lock on list
while (list.size() == maxSize) {
list.wait(); // wait on list
}
list.add(value);
list.notifyAll();
}
}
Note
This is a bounded list
No other operation is being performed apart from enqueue and dequeue.
I could use a blocking queue, but this question is more for improving my limited knowledge of threading.
If this question is repeated please let me know.

The short answer is, no, there is no functional difference, other than the extra memory overhead of maintaining that extra lock object. However, there are a couple of semantics-related items I would consider before making a final decision.
Will I ever need to perform synchronized operations on more than just my internal list?
Let's say you wanted to maintain a parallel data structure to your ArrayList, such that all operations on the list and that parallel data structure needed to be synchronized. In this case, it might be best to use the external lock, as locking on either the list or the structure might be confusing to future development efforts on this class.
Will I ever give access to my list outside of my queue class?
Let's say you wanted to provide an accessor method for your list, or make it visible to extensions of your Queue class. If you were using an external lock object, classes that retrieved references to the list would never be able to perform thread-safe operations on that list. In that case, it'd be better to synchronize on the list and make it clear in the API that external accesses/modifications to the list must also synchronize on that list.
I'm sure there are more reasons why you might choose one over the other, but these are the two big ones I can think of.

Related

Is following code Thread safe

I have a scenario where i have to maintain a Map which can be populated by multiple threads ,each modifying there respective List (unique identifier/key being thread name) and when the list size for a thread exceeds a fixed batch size we have to persist the records in DB.
Sample code below:
private volatile ConcurrentHashMap<String, List<T>> instrumentMap = new ConcurrentHashMap<String, List<T>>();
private ReadWriteLock lock ;
public void addAll(List<T> entityList, String threadName) {
try {
lock.readLock().lock();
List<T> instrumentList = instrumentMap.get(threadName);
if(instrumentList == null) {
instrumentList = new ArrayList<T>(batchSize);
instrumentMap.put(threadName, instrumentList);
}
if(instrumentList.size() >= batchSize -1){
instrumentList.addAll(entityList);
recordSaver.persist(instrumentList);
instrumentList.clear();
} else {
instrumentList.addAll(entityList);
}
} finally {
lock.readLock().unlock();
}
}
There is one more separate thread running after every 2 minutes to persist all the records in Map (to make sure we have something persisted after every 2 minutes and map size does not gets too big) and when it starts it block all other threads (check the readLock and writeLock usawhere writeLock has higher priority)
if(//Some condition) {
Thread.sleep(//2 minutes);
aggregator.getLock().writeLock().lock();
List<T> instrumentList = instrumentMap .values().stream().flatMap(x->x.stream()).collect(Collectors.toList());
if(instrumentList.size() > 0) {
saver.persist(instrumentList);
instrumentMap .values().parallelStream().forEach(x -> x.clear());
aggregator.getLock().writeLock().unlock();
}
This solution is working fine almost for every scenario we tested except sometime we see some of the records went missing i.e. not persisted at all although they were added fine in Map
My question is what is the problem with this code?
Is ConcurrentHashMap not the best solution here?
Does usage of read/write lock has some problem here?
Should i go with sequential processing?
No, it's not thread safe.
The problem is that you are using the read lock of the ReadWriteLock. This doesn't guarantee exclusive access for making updates. You'd need to use the write lock for that.
But you don't really need to use a separate lock at all. You can simply use the ConcurrentHashMap.compute method:
instrumentMap.compute(threadName, (tn, instrumentList) -> {
if (instrumentList == null) {
instrumentList = new ArrayList<>();
}
if(instrumentList.size() >= batchSize -1) {
instrumentList.addAll(entityList);
recordSaver.persist(instrumentList);
instrumentList.clear();
} else {
instrumentList.addAll(entityList);
}
return instrumentList;
});
This allows you to update items in the list whilst also guaranteeing exclusive access to the list for a given key.
I suspect that you could split the compute call into computeIfAbsent (to add the list if one is not there) and then a computeIfPresent (to update/persist the list): the atomicity of these two operations is not necessary here. But there is no real point in splitting them up.
Additionally, instrumentMap almost certainly shouldn't be volatile. Unless you really want to reassign its value (given this code, I doubt that), remove volatile and make it final.
Similarly, non-final locks are questionable too. If you stick with using a lock, make that final too.

Iterating over single List in parallel without duplicates in Java

I have an ArrayList filled with 'someObject'. I need to iterate over this list, with 4 different threads (using Futures & Callables). The threads will keep the top 5 valued objects it comes across. I first tried creating a parallel stream, but that didn't work out so well. Is there some obvious thing I'm not thinking of, so each thread can iterate over the objects, without possibly grabbing the same object twice?
You can use an AtomicInteger to iterate over the list:
class MyRunnable implements Runnable {
final List<SomeObject> list;
final AtomicInteger counter; // initialize to 0
public void run() {
while(true) {
int index = counter.getAndIncrement();
if(index < list.size()) {
do something with list.get(index);
} else {
return;
}
}
}
}
So long as each MyRunnable has the same AtomicInteger reference they won't duplicate indices
You don't need AtomicInteger or any other synchronization for that matter.
You should simply logically partition your list (whose size is known upfront) based on the number of processing threads (whose number is also known upfront) and let each of them operate on its own section of [from, to) of the list.
This avoid the need for any synchronization at all (even if it's just an optimized one such as AtomicInteger) which is what you should always strive for (as long as it's safe).
Pseudo code
class Worker<T> implements Runnable {
final List<T> toProcess;
protected Worker(List<T> list, int fromInc, int toExcl){
// note this does not allow passing an empty list or specifying an empty work section but you can relax that if you wish
// this also implicitly checks the list for null
Preconditions.checkArgument(fromInc >= 0 && fromInc < list.size());
Preconditions.checkArgument(toExcl > 0 && fromInc <= list.size());
// note: this does not create a copy, but only a view so it's very cheap
toProcess = list.subList(fromInc, toExcl);
}
#Override
public final void run() {
for(final T t : toProcess) {
process(t);
}
}
protected abstract process(T t);
}
As with the AtomicInteger solution (really any solution which does not involve copying the list), this solution also assumes that you will not be modifying the list once you have handed it off to each thread and processing has commenced. Modifying the list while processing is in progress will result in undefined behavior.

Thread-safe serializable Collection with atomic replace

I am facing a problem in my program when multiple threads access the same server over RMI. The server contains a list as a cache and performs some expensive computation sometimes changing that list. After the computation finished the list will be serialized and sent to the client.
First Problem: if the list is changed while being serialized (e.g. by a different client requesting some data) a ConcurrentModificationException is (probably) thrown, resulting in a EOFException for the RMI call / the deserialization on the client-side.
Therefore I need a some kind of list-structure which is "stable" for serialization while possibly being changed by a different thread.
Solutions we tried:
regular ArrayList / Set - not working because of concurrency
deep-copying the entire structure before every serialization - faaar too expensive
CopyOnWriteArrayList - expensive as well since it copies the list and
revealing the Second Problem: we need to be able to atomically replace any element in the list which is currently not thread-safe (first delete, then add (which is even more expensive)) or only doable by locking the list and therefore only doing the different threads in sequence.
Therefore my question is:
Do you know of a Collection implementation which allows us to serialize the Collection thread-safe while other Threads modify it and which contains some way of atomically replacing elements?
A bonus would be if the list would not need to be copied before serialization! Creating a snapshot for every serialization would be okay, but still meh :/
Illustration of the problem (C=compute, A=add to list, R=remove from list, S=serialize)
Thread1 Thread2
C
A
A C
C A
S C
S R <---- Remove and add have to be performed without Thread1 serializing
S A <---- anything in between (atomically) - and it has to be done without
S S blocking other threads computations and serializations for long
S and not third thread must be allowed to start serializing in this
S in-between state
S
The simplest solution would be to imply external synchronization to the ArrayList, possibly via read-write lock like this:
public class SyncList<T> implements Serializable {
private static final long serialVersionUID = -6184959782243333803L;
private List<T> list = new ArrayList<>();
private transient Lock readLock, writeLock;
public SyncList() {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readLock = readWriteLock.readLock();
writeLock = readWriteLock.writeLock();
}
public void add(T element) {
writeLock.lock();
try {
list.add(element);
} finally {
writeLock.unlock();
}
}
public T get(int index) {
readLock.lock();
try {
return list.get(index);
} finally {
readLock.unlock();
}
}
public String dump() {
readLock.lock();
try {
return list.toString();
} finally {
readLock.unlock();
}
}
public boolean replace(T old, T newElement) {
writeLock.lock();
try {
int pos = list.indexOf(old);
if (pos < 0)
return false;
list.set(pos, newElement);
return true;
} finally {
writeLock.unlock();
}
}
private void writeObject(ObjectOutputStream out) throws IOException {
readLock.lock();
try {
out.writeObject(list);
} finally {
readLock.unlock();
}
}
#SuppressWarnings("unchecked")
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
list = (List<T>) in.readObject();
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readLock = readWriteLock.readLock();
writeLock = readWriteLock.writeLock();
}
}
Provide any operations you like, just properly use either read-lock or write-lock.
My wrong initial thought was that the CopyOnWriteArrayList was a bad idea since it copies everything. But of course it does only perform a shallow copy, copying only the references, not a deep copy copying all Objects as well.
Therefore we clearly went with the CopyOnWriteArrayList because it already offered a lot of the needed functionality. The only remaining problem was the replace which even got more complex to be a addIfAbsentOrReplace.
We tried the CopyOnWriteArraySet but that did not fit our need because it only offered addIfAbsent. But in our case we had a instance of a class C called c1 which we needed to store and then replace with a updated new instance c2. Of course we overwrite equals and hashCode. Now we had to choose wether or not we wanted the equality to return true or false for the two only minimally different objects. Both options did not work, because
true would mean that the objects are the same and the set would not even bother adding the new object c2 because c1 already is in
false would mean c2 would be added but c1 would not be removed
Therefore CopyOnWriteArrayList. That list already offers a
public void replaceAll(UnaryOperator<E> operator) { ... }
which somewhat fits our needs. It lets us replace the object we need via custom comparison.
We utilized it in the following way:
protected <T extends OurSpecialClass> void addIfAbsentOrReplace(T toAdd, List<T> elementList) {
OurSpecialClassReplaceOperator<T> op = new OurSpecialClassReplaceOperator<>(toAdd);
synchronized (elementList) {
elementList.replaceAll(op);
if (!op.isReplaced()) {
elementList.add(toAdd);
}
}
}
private class OurSpecialClassReplaceOperator<T extends OurSpecialClass> implements UnaryOperator<T> {
private boolean replaced = false;
private T toAdd;
public OurSpecialClassReplaceOperator(T toAdd) {
this.toAdd = toAdd;
}
#Override
public T apply(T toAdd) {
if (this.toAdd.getID().equals(toAdd.getID())) {
replaced = true;
return this.toAdd;
}
return toAdd;
}
public boolean isReplaced() {
return replaced;
}
}

List ConcurrentModificationException in servlet

It's plenty of questions regarding ConcurrentModificationException for ArrayList objects, but I could not find yet an answer to my problem.
In my servlet I have an ArrayList as a member object:
List myList<Object> = new ArrayList<Object> (...);
The list must be shared among users and sessions.
In one of the servlet's methods, method1, I need to iterate over the ArrayList items, and eventually add clear the list after the iteration. Here a snippet:
for (Object o : myList) {
// read item o
}
myList.clear();
In another method, method2, I simply add a new Item to the list.
Most of the times the method ends its job without errors. Sometimes, probably due to the concurrent invocation of this method by different users, I get the famous java util.ConcurrentModificationException exception.
Should I define my List as:
List myList = Collections.synchronizedList(new ArrayList(...));
Would this be enough or am I missing something? What's behind the scenes? When there is a possible concurrency, is the second thread held in standby by the container?
EDIT: I have added the answers to some comments.
Using a synchronized list will not solve your problem. The core of the problem is that you are iterating over a list and modifying it at the same time. You need to use mutual exclusion mechanisms (synchronized blocks, locks etc) to ensure that they do not happen at the same time. To elaborate, if you start with:
methodA() {
iterate over list {
}
edit list;
}
methodB() {
edit list;
}
If you use a synchronized list, what you essentially get is:
methodA() {
iterate over list {
}
synchronized {
edit list;
}
}
methodB() {
synchronized {
edit list;
}
}
but what you actually want is:
methodA() {
synchronized {
iterate over list {
}
edit list;
}
}
methodB() {
synchronized {
edit list;
}
}
Just using synchronizedList makes all methods thread safe EXCEPT Iterators.
I would use CopyOnWriteArrayList. It is thread safe and doesn't produce ConcurrentModificationException.
ConcurrentModificaitonException occurs when you attempt to modify a collection while you're iterating through it. I imagine that the error only gets thrown when you perform some conditional operation.
I'd suggest pushing the values you want to add/remove into a separate list and performing the add /remove after you're done iterating.
You need to lock not just over the method accesses but over your use of the list.
So if you allocate a paired Object like:
Object myList_LOCK = new Object();
then you can lock that object whenever you are accessing the List, like this:
synchronized(myList_LOCK) {
//Iterate through list AND modify all within the same lock
}
at the moment the only locking you're doing is within the individual methods of the List, which isn't enough in your case because you need atomicity over the entire sequence of iteration and modification.
You could use the actual object (myList) to lock rather than a paired object but in my experience you are better off using another dedicated object as it avoids unexpected deadlock conditions that can arise as a result of the code internal to the object locking on the object itself.
This is kind of an add onto Peter Lawery's answer. But since copying wouldn't effect you too negatively you can do a mixture of copying with synchronization.
private final List<Object> myList = new ArrayList<Object>();
public void iterateAndClear(){
List<Object> local = null;
synchronized(myList){
local = new ArrayList<Object>(myList);
myList.clear();
}
for(Object o : local){
//read o
}
}
public void add(Object o){
synchronized(myList){
myList.add(o);
}
}
Here you can iterate over o elements without fear of comodifications (and outside of any type of synchronization), all while myList is safely cleared and added to.

Java concurrency question - synchronizing on a collection

Will the following code snippet of a synchronized ArrayList work in a multi-threaded environment?
class MyList {
private final ArrayList<String> internalList = new ArrayList<String>();
void add(String newValue) {
synchronized (internalList) {
internalList.add(newValue);
}
}
boolean find(String match) {
synchronized (internalList) {
for (String value : internalList) {
if (value.equals(match)) {
return true;
}
}
}
return false;
}
}
I'm concerned that one thread wont be able to see changes by another thread.
Your code will work and is thread-safe but not concurrent. You may want to consider using ConcurrentLinkedQueue or other concurrent thread-safe data structures like ConcurrentHashMap or CopyOnWriteArraySet suggested by notnoop and employ contains method.
class MyList {
private final ConcurrentLinkedQueue<String> internalList =
new ConcurrentLinkedQueue<String>();
void add(String newValue) {
internalList.add(newValue);
}
boolean find(String match) {
return internalList.contains(match);
}
}
This should work, because synchronizing on the same object establishes a happens-before relationship, and writes that happen-before reads are guaranteed to be visible.
See the Java Language Specification, section 17.4.5 for details on happens-before.
It will work fine, because all access to the list is synchronized. Hovewer, you can use CopyOnWriteArrayList to improve concurrency by avoiding locks (especially if you have many threads executing find).
It will work, but better solution is to create a List by calling Collections.synchronizedList().
You may want to consider using a Set(Tree or Hash) for your data as you are doing lookups by a key. They have methods that will be much faster than your current find method.
HashSet<String> set = new HashSet<String>();
Boolean result = set.contains(match); // O(1) time

Categories

Resources