Is it safe to iterate over synchronized wrappers?
Asked Answered
A

1

5

I decided to dig into source code a bit and noticed that Collections.synchronizedList(List) is implemented as follows:

public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
        new SynchronizedRandomAccessList<T>(list) :
        new SynchronizedList<T>(list));
}

where the SynchronizedList nested class is:

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 More ...equals(Object o) {
        synchronized(mutex) {return list.equals(o);}
    }

    //ommited

    public void add(int index, E element) {
        synchronized(mutex) {list.add(index, element);}
    }

    public E remove(int index) {
        synchronized(mutex) {return list.remove(index);}
    }

    //rest is ommited
}

As can bee seen, the class useses a private lock object to provide thread-safety. But the documentation allows us to iterate over it using locking on the objetct returned by the factory method.

It is imperative that the user manually synchronize on the returned list when iterating over it:

So, we use different locks for iterating and modifying list (add, remove, etc).

Why is it considered to be safe?

Anagnos answered 4/1, 2016 at 7:16 Comment(2)
So, we use different locks for iterating and modifying list What makes you think so?Leix
@SotiriosDelimanolis The source code I quoted. In the nested class we use synchronized(mutex) which is not accesible from outside.Anagnos
L
7

The Collections#synchronizedList method

public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
            new SynchronizedRandomAccessList<>(list) :
            new SynchronizedList<>(list));
}

uses that single parameter constructor that you've shown in your question. That constructor invokes the super constructor which sets this as the mutex. All the methods are synchronized on mutex, this.

The documentation is telling you to also synchronize on the instance while iterating. That reference is the same as the this within the method bodies.

All these actions are (should, if you do it right) therefore sharing the same lock.

Leix answered 4/1, 2016 at 7:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.