-1

Let's say, We're trying to add several elements into an ArrayList. At the same time, the same list is being searched for list.contains(givenElement).

Then, how do we achieve thread-safety without locking the whole list object? In other words, how can multiple threads access this list and perform operations atomically? I would like to know how to achieve this using AtomicReference.

I can create an AtomicReference variable with the list loaded in it. But, then how do I do contains() operation atomically without doing compareAndSet() on whole list ?

Note that I'm looking for solutions other than using thread-safe collections.

Rahul Raj
  • 3,197
  • 5
  • 35
  • 55
  • Consider using a [thread-safe `Set`](https://stackoverflow.com/q/6720396/642706). – Basil Bourque Mar 31 '23 at 19:44
  • 2
    `AtomicReference` protects the *reference* to the `List` object, not the *contents* of the `List`. Read the related links to learn about thread-safe `List` & `Set` implementations that protect the elements. – Basil Bourque Mar 31 '23 at 19:45
  • @BasilBourque exactly my point. So, can we achieve this using any of the `Atomic` package from Java? May be by using a combinations of them? – Rahul Raj Mar 31 '23 at 19:46
  • 2
    None of the `Atomic…` classes protect the elements of a collection. You are [barking up the wrong tree](https://en.wikipedia.org/wiki/Barking_up_the_wrong_tree). – Basil Bourque Mar 31 '23 at 19:47
  • @BasilBourque How about `ReadWriteLock`? There will be changes and searches happening concurrently. So, the only way is to use a thread-safe collection unless we need to lock the entire list? – Rahul Raj Mar 31 '23 at 19:50
  • Re, "it a mandatory thing that AtomicReference expects an immutable object?" No. There is no requirement that the referent of an [`AtomicReference`](https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/concurrent/atomic/AtomicReference.html) be immutable. As far as I know, "immutable object" is not even a concept that the Java language recognizes. That is to say, there is no way (that I know of) to define some type `Foobar` and have the compiler enforce that `V` must be immutable. – Solomon Slow Apr 01 '23 at 11:55
  • Re, "I want to know the behavior when a custom mutable object is used." Behavior of what? Of the AtomicReference? An AtomicReference does not interact with its referent in any way. There are no two types `Foo` and `Bar` such that an `AtomicReference` would behave any differently from an `AtomicReference`. – Solomon Slow Apr 01 '23 at 12:07
  • Re, "... Can we wrap that object with AtomicReference?" What does "wrap" mean? If `AtomicReference` "wraps" `T`, then sure. You can "wrap" _any_ type with AtomicReference. You can have AtomicReference if that solves some problem for you. I don't care to imagine what problem it would solve, but Java won't stop you from doing it. You could even have AtomicReference>>. – Solomon Slow Apr 01 '23 at 12:11
  • @SolomonSlow `As far as I know, "immutable object" is not even a concept that the Java language recognizes.` --> what do you mean?? – Rahul Raj Apr 01 '23 at 17:06
  • 1
    What I mean is, If I define some API, and if my API allows you to submit instances of some class that you declare in your own code, then I don't know any way I can prevent you from submitting _mutable_ instances. I can tell you not to do it in my documentation. And maybe, under some circumstances, I could notice that you have mutated an instance after you gave me and, I could raise an exception; but there's nothing I can do to outright _prevent_ you from giving me a mutable instance in the first place. Java's type machinery has no way of specifying an immutable data type. – Solomon Slow Apr 01 '23 at 18:22

1 Answers1

2

If the list itself doesn't change then you should not be looking at AtomicReference.

It looks like you need ReadWriteLock, allowing multiple threads to check the list at once while locking during changes to the list.

To check if it is in the list you need Lock readLock() and to change it Lock writeLock().

There are many examples for this kind of usage.

After comment from OP: Changes not just compare can be made.

Get the readLock, see if it is contained in the list, then get the writeLock if you have to add it to the list. You only acquire the writeLock if a change is needed.

Bill Mair
  • 1,073
  • 6
  • 15
  • Yes, the list keeps changing and new values come on every instant. That's why I was looking for `AtomicReference` And preferably, don't want to lock the entire list. – Rahul Raj Mar 31 '23 at 19:40
  • With a "readLock", multiple threads can read at the same time. It is, by design, a "non blocking" read lock. – Bill Mair Mar 31 '23 at 19:41
  • Get the readLock, see if it is contained in the list, then get the writeLock if you have to add it to the list. You only acquire the writeLock if a change is needed. – Bill Mair Mar 31 '23 at 19:44
  • Thank you for the insights! so if a thread acquired `writeLock`, then other threads can't read right? – Rahul Raj Mar 31 '23 at 19:52
  • See: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/locks/ReadWriteLock.html and https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.html – Bill Mair Mar 31 '23 at 19:57
  • @BillMair, Re, "...then get the writeLock if you have to add..." But note! If that's `java.util.concurrent.locks.ReadWriteLock` interface that you're talking about, then the documentation explicitly does not specify whether read ownership can be upgraded to write ownership. If the OP chooses the standard `ReentrantReadWriteLock` implementation of that interface, then _that_ doc page explicitly says that upgrading is _not_ possible. So, OP will have to release the read permission, then acquire write permission, and then _re-test_ to see whether the item _still_ is not present in the list. – Solomon Slow Mar 31 '23 at 22:06
  • @SolomonSlow I didn't say "upgrade", you can acquire the writeLock independently of any other readLocks that you might have. Releasing the readLock is not a requirement, having already gained the readLock, to acquiring the writeLock. Being able to write is not mutually exclusive to being already able to read. – Bill Mair Mar 31 '23 at 22:22
  • You did't say "upgrade." You said, "Get the readLock, ..., then get the writeLock..." That's ambiguous. If "get the writeLock" means getting it without first _releasing_ the previously acquired readLock, then that would be an upgrade. If, on the other hand, you intended for the OP to release the readLock before attempting to get the writeLock, then that's not an upgrade, but it creates an opportunity for some other thread to get the writeLock first, and modify the list—a potential race condition. OP is a newbie. If you're leading them down a dangerous path, then you should warn them about it. – Solomon Slow Apr 01 '23 at 00:32
  • @SolomonSlow I had the same concern, thats why I specifically asked for lock-free atomic solutions (if possible) other than readymade thread-safe collections. Is there a guarantee of lock-free concurrency even if we use thread-safe collections? – Rahul Raj Apr 01 '23 at 05:58
  • @SolomonSlow Also, is it a mandatory thing that `AtomicReference` expects an `immutable` object? I want to know the behavior when a custom `mutable` object is used. I understand that `atomicity` is only for the `reference` of objects. Let's say an object with an `AtomicInteger` as member variable. Can we wrap that object with `AtomicReference` ? – Rahul Raj Apr 01 '23 at 06:09