Collections.synchronizedXXX() and internal mutex (aka SyncRoot)
dmytro sheyko
dmytro.sheyko.jdk at gmail.com
Fri May 1 09:02:45 UTC 2020
Thank you, I got it. I have one more a bit unrelated question.
The checked, synchronized and unmodifiable wrappers in some cases store
backing collection in more than one fields.
Thus `UnmodifiableList<E>` has
1. its own field `List<? extends E> list` and
2. `Collection<? extends E> c`, which it inherits from
`UnmodifiableCollection<E>`.
Also `UnmodifiableNavigableSet<E>` has even 3 duplicated fields:
1. its own `NavigableSet<E> ns`,
2. `SortedSet<E> ss` from `UnmodifiableSortedSet<E>` and
3. `Collection<? extends E> c` from `UnmodifiableCollection<E>`.
Isn't it worth to get rid of such duplication? (at least for unmodifiable
collections). This may affect serialization, but I believe it's still
possible preserve serialization backward compatible, if it's necessary.
Or is it done intentionally?
Thank you,
Dmytro
On Fri, May 1, 2020 at 1:20 AM Stuart Marks <stuart.marks at oracle.com> wrote:
> The general rule in the collections that wrappers and views don't divulge
> their
> backing collections. The reason is fairly obvious for things like the
> checked
> wrappers and unmodifiable wrappers, but it's equally important for various
> view
> collections like those of Lists, Sets, and Maps. If there were something
> like
> getSyncRoot() it could be used to break encapsulation. For example:
>
> Map<K, V> map = Collections.synchronizedMap(...);
> someMethod(map.values());
>
> Currently, the caller is assured that someMethod() can only see the values
> of
> the map. Also, as you observe, someMethod() can't safely iterate that
> collection
> using an iterator or stream. If getSyncRoot() were introduced to address
> that issue,
>
> void someMethod(Collection<V> values) {
> @SuppressWarnings("unchecked")
> var map = (Map<K,V>) Collections.getSyncRoot(values);
> // I now have access to map's keys and can put new entries,
> mwahaha!
> }
>
> Undoubtedly there are ways we can avoid this, but the cost is designing
> yet more
> complicated APIs in an area where it provides little value. I think it's
> pretty
> unlikely that we'll do anything like this, or variants that allow the
> caller to
> provide an external lock at creation time, such as proposed in
> JDK-4335520.
> There's a pretty fundamental clash between external locking and
> encapsulation,
> and the platform has shifted to pursuing other approaches for concurrency.
>
> Legacy code that needs to iterate collection views might find some luck
> replacing iterator loops with bulk methods like forEach() or removeIf().
>
> s'marks
>
>
> On 4/30/20 1:34 AM, dmytro sheyko wrote:
> > Hi Stuart,
> >
> > In general I agree with you regarding synchronized collections. But
> nevertheless
> > there is a lot of legacy code that still uses synchronized wrappers. And
> > sometimes synchronization is done on wrong lock object, which leads to
> > relatively rare but extremely hard to reproduce and troubleshoot errors.
> > Reworking whole this legacy stuff is risky. But if we had means to get
> the right
> > lock object for given synchronized collections, this would help us to
> make the
> > fixes more localized and hence safe. That's why I would like to know the
> reason,
> > why this has not been done earlier, and is there hope/plan this will be
> done in
> > near future.
> >
> > Thank you,
> > Dmytro
> >
> > On Thu, Apr 30, 2020 at 6:36 AM Stuart Marks <stuart.marks at oracle.com
> > <mailto:stuart.marks at oracle.com>> wrote:
> >
> > Hi Dmytro,
> >
> > Callers of an API performing explicit synchronization, along with the
> > synchronized collections wrappers, have mostly fallen into disuse
> since the
> > introduction of the java.util.concurrent collections.
> >
> > Multiple threads can either interact directly on a concurrent
> collection, or
> > the
> > developer can provide an intermediate object (not a collection) that
> does
> > internal locking, and that exports the right set of thread-safe APIs
> to
> > callers.
> > I'm thus skeptical of the utility of enhancing these wrapper classes
> with
> > additional APIs.
> >
> > Do you have a use case that's difficult to handle any other way?
> >
> > s'marks
> >
> >
> >
> > On 4/29/20 12:58 AM, dmytro sheyko wrote:
> > > Hello,
> > >
> > > Have you ever discussed to make field mutex in synchronized
> collections
> > > accessible?
> > >
> > > Javadoc for Collections#synchronizedSortedSet suggest to iterate
> collection
> > > this way:
> > >
> > > SortedSet s = Collections.synchronizedSortedSet(new TreeSet());
> > > SortedSet s2 = s.headSet(foo);
> > > ...
> > > synchronized (s) { // Note: s, not s2!!!
> > > Iterator i = s2.iterator(); // Must be in the synchronized
> block
> > > while (i.hasNext())
> > > foo(i.next());
> > > }
> > >
> > > I.e. in order to iterate subset, we also need a reference to the
> whole set,
> > > which is not really convenient. How about to make it possible to
> write:
> > >
> > > SortedSet s2 = s.headSet(foo);
> > > ...
> > > synchronized (Collections.getSyncRoot(s2)) { // Note:
> > > Collections.getSyncRoot(s2)
> > > Iterator i = s2.iterator(); // Must be in the synchronized
> block
> > > while (i.hasNext())
> > > foo(i.next());
> > > }
> > >
> > > Also I think it would be convenient to let to provide custom sync
> root when
> > > synchronized collection is created.
> > > E.g.
> > >
> > > Object customSyncRoot = new Object();
> > > SortedSet s = Collections.synchronizedSortedSet(new TreeSet(),
> > > customSyncRoot);
> > >
> > > What do you think about this?
> > >
> > > Regards,
> > > Dmytro
> > >
> >
>
More information about the core-libs-dev
mailing list