Java collection tools Collections. SynchronizedList provides a collection of thread safety packaging method. So how does it make a collection thread-safe? Why are thread-safe collections so inefficient to implement?

The realization of the synchronizedList

Let’s look at the source code.

public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list)); } /** * @serial include */ static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> { private static final long serialVersionUID = -7754090372962971524L; final List<E> list; SynchronizedList(List<E> list) { super(list); this.list = list; } SynchronizedList(List<E> list, Object mutex) { super(list, mutex); this.list = list; } public boolean equals(Object o) { if (this == o) return true; synchronized (mutex) {return list.equals(o); } } public int hashCode() { synchronized (mutex) {return list.hashCode(); } } public E get(int index) { synchronized (mutex) {return list.get(index); } } public E set(int index, E element) { synchronized (mutex) {return list.set(index, element); } } public void add(int index, E element) { synchronized (mutex) {list.add(index, element); } } public E remove(int index) { synchronized (mutex) {return list.remove(index); } } public int indexOf(Object o) { synchronized (mutex) {return list.indexOf(o); } } public int lastIndexOf(Object o) { synchronized (mutex) {return list.lastIndexOf(o); } } public boolean addAll(int index, Collection<? extends E> c) { synchronized (mutex) {return list.addAll(index, c); } } public ListIterator<E> listIterator() { return list.listIterator(); // Must be manually synched by user } public ListIterator<E> listIterator(int index) { return list.listIterator(index); // Must be manually synched by user } public List<E> subList(int fromIndex, int toIndex) { synchronized (mutex) { return new SynchronizedList<>(list.subList(fromIndex, toIndex), mutex); } } @Override public void replaceAll(UnaryOperator<E> operator) { synchronized (mutex) {list.replaceAll(operator); } } @Override public void sort(Comparator<? super E> c) { synchronized (mutex) {list.sort(c); } } /** * SynchronizedRandomAccessList instances are serialized as * SynchronizedList instances to allow them to be Deserialized * in the pre - 1.4 JREs (which do not have SynchronizedRandomAccessList). * This method inverts the transformation. As a beneficial * side-effect, It also grafts the RandomAccess marker onto * SynchronizedList instances that were serialized in pre-1.4jres. * * Note:  Unfortunately, SynchronizedRandomAccessList instances * serialized in the 1.4.1 and deserialized in 1.4 will become * SynchronizedList Instances, as this method was missing in 1.4. */ private Object readResolve() {return (list instanceof RandomAccess? new SynchronizedRandomAccessList<>(list) : this); }}Copy the code

When introduced into the parameter type is the ArrayList synchronizedList, because ArrayList implements RandomAccess interface, so synchronizedList will build a SynchronizedRandomAccessList object, But it doesn’t matter, is SynchronizedList SynchronizedRandomAccessList parent class, we see him directly.

List objects directly maintain arguments of type list that are passed in, while implementations in methods like GET set add remove are wrapped in the thread-synchronization block synchronized (mutex). So who is this mutex lock? It looks like super(list); SynchronizedCollection is the parent of SynchronizedList

/** * @serial include */ static class SynchronizedCollection<E> implements Collection<E>, Serializable { private static final long serialVersionUID = 3053995032091335093L; final Collection<E> c; // Backing Collection final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection<E> c) { this.c = Objects.requireNonNull(c); mutex = this; } SynchronizedCollection(Collection<E> c, Object mutex) { this.c = Objects.requireNonNull(c); this.mutex = Objects.requireNonNull(mutex); } public int size() { synchronized (mutex) {return c.size(); } } public boolean isEmpty() { synchronized (mutex) {return c.isEmpty(); } } public boolean contains(Object o) { synchronized (mutex) {return c.contains(o); } } public Object[] toArray() { synchronized (mutex) {return c.toArray(); } } public <T> T[] toArray(T[] a) { synchronized (mutex) {return c.toArray(a); } } public Iterator<E> iterator() { return c.iterator(); // Must be manually synched by user! } public boolean add(E e) { synchronized (mutex) {return c.add(e); } } public boolean remove(Object o) { synchronized (mutex) {return c.remove(o); } } public boolean containsAll(Collection<? > coll) { synchronized (mutex) {return c.containsAll(coll); } } public boolean addAll(Collection<? extends E> coll) { synchronized (mutex) {return c.addAll(coll); } } public boolean removeAll(Collection<? > coll) { synchronized (mutex) {return c.removeAll(coll); } } public boolean retainAll(Collection<? > coll) { synchronized (mutex) {return c.retainAll(coll); } } public void clear() { synchronized (mutex) {c.clear(); } } public String toString() { synchronized (mutex) {return c.toString(); } } // Override default methods in Collection @Override public void forEach(Consumer<? super E> consumer) { synchronized (mutex) {c.forEach(consumer); } } @Override public boolean removeIf(Predicate<? super E> filter) { synchronized (mutex) {return c.removeIf(filter); } } @Override public Spliterator<E> spliterator() { return c.spliterator(); // Must be manually synched by user! } @Override public Stream<E> stream() { return c.stream(); // Must be manually synched by user! } @Override public Stream<E> parallelStream() { return c.parallelStream(); // Must be manually synched by user! } private void writeObject(ObjectOutputStream s) throws IOException { synchronized (mutex) {s.defaultWriteObject(); }}}Copy the code

We can clearly see that mutex = this; The lock is the object itself! SynchronizedList implements thread safety by locking itself forcibly.

After obtaining a safe list, why does the outer layer use synchronized?

This is how synchronizedList is used in official documentation:

List list = Collections.synchronizedList(new ArrayList()); . synchronized (list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }Copy the code

The question arises: why add a layer of object locking to the wrapper class when it already has an object lock inside?

[# Understanding Collections and Thread Safety in Java](www.codejava.net/java-core/c…) NOTE: When using the iterator of a synchronized collection, we should use synchronized block to safeguard the iteration code because the iterator itself is not thread-safe. Consider the following code:

List<String> safeList = Collections.synchronizedList(new ArrayList<>());
 
// adds some elements to the list
 
Iterator<String> iterator = safeList.iterator();
 
while (iterator.hasNext()) {
    String next = iterator.next();
    System.out.println(next);
}
Copy the code

Although the safeList is thread-safe, its iterator is not, so we should manually add synchronized block like this:

synchronized (safeList) { while (iterator.hasNext()) { String next = iterator.next(); System.out.println(next); }}Copy the code

Iterator () is read from a list that hasNext() does not have. When i.next() is called, another thread removes it. This synchronized block is intended to safeguard the concurrency problem of these three lines executing simultaneously in multiple threads.

As for synchronizedList internal lock, is that at the time of the concurrent execution of the add/remove, don’t put the multiple threads within the things added to the list of the same position, lead to problems such as loss of data or dirty data, This is to ensure that the List does not have concurrency problems when performing add/remove.

In short, the two locks are concurrency problems at different levels.

Synchronized (list) is added to the synchronizedList to ensure thread safety when traversing the synchronizedList.