Synchronized Observable List
Richard Bair
richard.bair at oracle.com
Mon Sep 24 07:18:48 PDT 2012
private static class SynchronizedList<T> implements List<T> {
final Object mutex;
private final List<T> backingList;
SynchronizedList(List<T> list, Object mutex) {
this.backingList = list;
this.mutex = mutex;
}
SynchronizedList(List<T> seq) {
this (seq, new Object());
}
@Override
public int size() {
synchronized(mutex) {
return backingList.size();
}
}
@Override
public boolean isEmpty() {
synchronized(mutex) {
return backingList.isEmpty();
}
}
@Override
public boolean contains(Object o) {
synchronized(mutex) {
return backingList.contains(o);
}
}
@Override
public Iterator<T> iterator() {
return backingList.iterator();
}
@Override
public Object[] toArray() {
synchronized(mutex) {
return backingList.toArray();
}
}
@Override
public <T> T[] toArray(T[] a) {
synchronized(mutex) {
return backingList.toArray(a);
}
}
@Override
public boolean add(T e) {
synchronized(mutex) {
return backingList.add(e);
}
}
@Override
public boolean remove(Object o) {
synchronized(mutex) {
return backingList.remove(o);
}
}
@Override
public boolean containsAll(Collection<?> c) {
synchronized(mutex) {
return backingList.containsAll(c);
}
}
@Override
public boolean addAll(Collection<? extends T> c) {
synchronized(mutex) {
return backingList.addAll(c);
}
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
synchronized(mutex) {
return backingList.addAll(index, c);
}
}
@Override
public boolean removeAll(Collection<?> c) {
synchronized(mutex) {
return backingList.removeAll(c);
}
}
@Override
public boolean retainAll(Collection<?> c) {
synchronized(mutex) {
return backingList.retainAll(c);
}
}
@Override
public void clear() {
synchronized(mutex) {
backingList.clear();
}
}
@Override
public T get(int index) {
synchronized(mutex) {
return backingList.get(index);
}
}
@Override
public T set(int index, T element) {
synchronized(mutex) {
return backingList.set(index, element);
}
}
@Override
public void add(int index, T element) {
synchronized(mutex) {
backingList.add(index, element);
}
}
@Override
public T remove(int index) {
synchronized(mutex) {
return backingList.remove(index);
}
}
@Override
public int indexOf(Object o) {
synchronized(mutex) {
return backingList.indexOf(o);
}
}
@Override
public int lastIndexOf(Object o) {
synchronized(mutex) {
return backingList.lastIndexOf(o);
}
}
@Override
public ListIterator<T> listIterator() {
return backingList.listIterator();
}
@Override
public ListIterator<T> listIterator(int index) {
synchronized(mutex) {
return backingList.listIterator(index);
}
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
synchronized(mutex) {
return new SynchronizedList<T>(backingList.subList(fromIndex, toIndex),
mutex);
}
}
}
private static class SynchronizedObservableList<T> extends SynchronizedList<T> implements ObservableList<T> {
private final ObservableList<T> backingList;
SynchronizedObservableList(ObservableList<T> seq, Object mutex) {
super(seq, mutex);
this.backingList = seq;
}
SynchronizedObservableList(ObservableList<T> seq) {
this(seq, new Object());
}
@Override
public boolean addAll(T... elements) {
synchronized(mutex) {
return backingList.addAll(elements);
}
}
@Override
public boolean setAll(T... elements) {
synchronized(mutex) {
return backingList.setAll(elements);
}
}
@Override
public boolean removeAll(T... elements) {
synchronized(mutex) {
return backingList.removeAll(elements);
}
}
@Override
public boolean retainAll(T... elements) {
synchronized(mutex) {
return backingList.retainAll(elements);
}
}
@Override
public void remove(int from, int to) {
synchronized(mutex) {
backingList.remove(from, to);
}
}
@Override
public boolean setAll(Collection<? extends T> col) {
synchronized(mutex) {
return backingList.setAll(col);
}
}
@Override
public final void addListener(InvalidationListener listener) {
backingList.addListener(listener);
}
@Override
public final void removeListener(InvalidationListener listener) {
backingList.removeListener(listener);
}
@Override
public void addListener(ListChangeListener<? super T> o) {
backingList.addListener(o);
}
@Override
public void removeListener(ListChangeListener<? super T> o) {
backingList.removeListener(o);
}
}
On Sep 22, 2012, at 2:58 PM, Bruce Alspaugh wrote:
> I'm new to JavaFX, so I was browsing the Javadoc for:
>
> FXCollections#synchronizedObservableList(ObservableList<?>)
>
> It doesn't say whether it synchronizes on the returned list or some
> other object that might not be accessible to me. Can I safely assume it
> synchronizes on the intrinsic lock of the wrapper?
>
> A potential problem with observable synchronized collections is that you
> have to be very careful not to call an "alien" method while holding a
> lock which risks deadlock and other synchronization problems as
> described in Item 67 of Effective Java. In this case, the listeners
> registered on the wrapper would be considered "alien" to JavaFX. If the
> wrapper naively holds the lock while forwarding the call to the
> corresponding method of the wrapped list, the lock would be held during
> the time the listeners are being notified, risking exceptions and
> deadlock.
>
> One approach to work around this issue would be to use a condition
> variable on the lock to temporarily suspend it while the registered
> listeners are notified, and then regain it after the listeners have been
> notified. This would require some sophistication and overhead to the
> underlying code that notifies listeners in JavaFX.
>
> I have concerns about the approach of moving the listener notifications
> outside the critical section and the CopyOnWriteArrayList approach as
> described in Effective Java Item 67, because you can't guarantee the
> order that listeners are notified when multiple threads concurrently
> modify the collection, and the listeners themselves would have to be
> able to handle concurrent notification. For example, if thread 1 makes
> change A and thread 2 makes change B to the same collection, you can't
> guarantee that a listener would be notified of A before B, B before A,
> or it could be concurrently notified of A and B. I also don't see how
> Dr. Bloch is guarding the backing Set from concurrent modification. I
> understand it could be a thread-safe Set, like CopyOnWriteArraySet, but
> there is nothing to guarantee it is so.
>
> I suppose the simplest approach of all would be to simply confine
> observable collections to a single thread. What is the use case that
> requires an observable collection to be concurrently modified? Could
> the use case be handled some other way?
>
> If someone could point me to where I can browse the JavaFX source code
> so I can see what is actually going on here, it would be quite helpful.
>
> Thanks,
>
> Bruce
>
More information about the openjfx-dev
mailing list