From this source one can read:
It's worth mentioning that synchronized and concurrent collections only make the collection itself thread-safe and not the contents.
I thought if Collection
is thread-safe then its content will implicitly be thread-safe.
I mean if two threads cannot access my Collection
object then the object which my Collection
object is holding will implicitly become thread-safe.
I missing the point, could someone please explain me with an example?
Synchronized collections achieve thread-safety through intrinsic locking, and the entire collections are locked. Intrinsic locking is implemented via synchronized blocks within the wrapped collection's methods.
The main reason for this slowness is locking; synchronized collections lock the whole collection e.g. whole Map or List while concurrent collection never locks the whole Map or List. They achieve thread safety by using advanced and sophisticated techniques like lock stripping.
The only two legacy collections are thread-safe: Vector and Hashtable.
This method accepts an object of Set interface and, returns a synchronized (thread-safe) set backed by the specified set. This method accepts an object of the Map interface and, returns a synchronized (thread-safe) sorted map backed by the specified sorted map.
The objects stored in a thread-safe collection can be leaked outside and used in a non-thread-safe manner.
Detailed Answer:
I thought if Collection is thread-safe then its content will implicitly be thread-safe, I mean if two threads cannot access my Collection object then the object which my Collection object is holding will implicitly become thread-safe.
I know sure I missing the point, could someone please explain me with an example.
Consider the following code that uses two threads to add to the same non-thread-safe list the elements from 0 to 10
. In the end, the main thread sums all the elements of that list. The final result should be same as 0 + 0 + 1 + 1 + ... 9 + 9 = 90.
However, if you execute the code a couple of times you get different values, and sometimes even the following NPE
:
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.stream.ReduceOps$1ReducingSink.accept(ReduceOps.java:80)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:553)
at Z.CollectionThreadSafe.main(CollectionThreadSafe.java:26)
All this is the result of the race-condition during the call of the method add
.
private static void addToList(List<Integer> list) {
for (int i = 0; i < 10; i++)
list.add(i);
}
public static void main(String[] arg) throws InterruptedException {
final int TOTAL_THREADS = 2;
List<Integer> list = new ArrayList<>();
ExecutorService pool = Executors.newFixedThreadPool(TOTAL_THREADS);
for (int i = 0; i < TOTAL_THREADS; i++) {
pool.submit(() -> addToList(list));
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println(list.stream().reduce(0, Integer::sum));
}
Let us fix the race-condition by using a thread-Safe List
by calling Collections.synchronizedList. So let us adapt the previous code to:
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
You can run it as many times as you want; the final result is always the same i.e., 90
. That much we knew already. Let us showcase the:
It's worth mentioning that synchronized and concurrent collections only make the collection itself thread-safe and not the contents.
You just need to adapt the previous code from:
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
to:
final List<List<Integer>> LIST_THREAD_SAFE = Collections.synchronizedList(new ArrayList<>());
LIST_THREAD_SAFE.add(new ArrayList<>());
List<Integer> list = LIST_THREAD_SAFE.get(0);
...
and voilá! you have exactly the same situation as the first example that we have showcased (i.e., race-condition). Even though the list LIST_THREAD_SAFE
is thread-safe its content is not. Hence,
synchronized and concurrent collections only make the collection itself thread-safe and not the contents.
If the collection is thread safe two thread can still access the collection simultaneously. They also could do modification simultaneously if this does not influence the the coherence of the collection. It is guaranteed only that all operation are consistent possibly with the minimum amount of locks. This has no relation with the object in the collection: one thread can get a reference of an object from the collection and keep for a certain amount of time and so other threads can get the same reference from the collection (in the same time o later) and access the object in the same time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With