是迭代通过Collections

是迭代通过Collections

本文介绍了是迭代通过Collections.synchronizedSet(...)。forEach()保证线程安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们知道,对并发集合的迭代在默认情况下不是线程安全的,所以不能使用:

  Set< E> set = Collections.synchronizedSet(new HashSet<>()); 
//用数据填充
(E e:set){
process(e);
}

这是因为在迭代过程中可能会添加数据, 设置。



Collections.synchronizedSet :

不适用于 Set.forEach ,它继承了。



现在我查看了源代码,这里我们可以看到我们有以下结构:


  1. 我们要求一个 Collections.synchronizedSet c $ c>。

  2. 我们得到一个:

      public static< ; T>设置< T> synchronizedSet(Set< T> s){
    return new SynchronizedSet(s);
    }

    ...

    静态类SynchronizedSet< E>
    extends SynchronizedCollection< E>
    implements Set< E> {
    private static final long serialVersionUID = 487447009682186044L;

    SynchronizedSet(Set< E> s){
    super(s);
    }
    SynchronizedSet(Set< E> s,Object mutex){
    super(s,mutex);
    }

    public boolean equals(Object o){
    if(this == o)
    return true;
    synchronized(mutex){return c.equals(o);}
    }
    public int hashCode(){
    synchronized(mutex){return c.hashCode
    }
    }


  3. SynchronizedCollection ,在显而易见的方法旁边有以下有趣的方法:

      //覆盖集合中的默认方法
    @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(); //必须由用户手动同步!
    }
    @Override
    public Stream< E> stream(){
    return c.stream(); //必须由用户手动同步!
    }
    @Override
    public Stream< E> parallelStream(){
    return c.parallelStream(); //必须由用户手动同步!
    }


$ c> mutex 与 Collections.synchronizedSet 锁定的所有操作是相同的对象。 / p>

现在我们可以通过实现来判断它是线程安全的使用 Collections.synchronizedSet ).forEach(...),但它也是线程安全的按规范



, Collections.synchronizedSet(...)。stream()。forEach(...)是不是线程安全的执行, )

解决方案

正如你写的,通过实现判断, forEach ()对于JDK 提供的集合是线程安全的(请参阅下面的免责声明),因为它需要获取互斥体的监控才能继续。

,这里是一个解释。 Collections.synchronizedXXX() javadoc,简写为所有方法都是线程安全的,除了用于迭代的线程。



我的另一个,虽然非常主观的参数是什么yshavit 写 - 除非被告知/读取,考虑API /类/不是线程安全的。



现在,让我们仔细看看javadocs。我想我可以说,方法 forEach()用于迭代它,所以,根据javadoc的意见,我们应该考虑它不线程安全,虽然它是与实际情况相反。



无论如何,我同意yshavit 的声明,文档应该更新,因为这很可能是一个文档,而不是实现缺陷。但是,除了JDK开发人员,没有人能肯定地说,请参阅下面的疑虑。



在本讨论中我想提到的最后一点 - 我们可以假设custom集合可以用 Collections.synchronizedXXX()包装,并且此集合的 forEach()的实现可以。 ..可以是任何东西。该集合可以对 forEach()方法中的元素执行异步处理,为每个元素生成一个线程...它仅由作者的想象力限定,并且同步(互斥量)不能保证此类情况的线程安全。这个特定的问题可能是不将 forEach()方法声明为线程安全的原因。


As we know, iterating over a concurrent collection is not thread safe by default, so one cannot use:

Set<E> set = Collections.synchronizedSet(new HashSet<>());
//fill with data
for (E e : set) {
    process(e);
}

This happens as data may be added during iteration, because there is no exclusive lock on set.

This is describe in the javadoc of Collections.synchronizedSet:

However, this does not apply to Set.forEach, which inherits the default method forEach from Iterable.forEach.

Now I looked into the source code, and here we can see that we have the following structure:

  1. We ask for a Collections.synchronizedSet().
  2. We get one:

    public static <T> Set<T> synchronizedSet(Set<T> s) {
        return new SynchronizedSet<>(s);
    }
    
    ...
    
    static class SynchronizedSet<E>
          extends SynchronizedCollection<E>
          implements Set<E> {
        private static final long serialVersionUID = 487447009682186044L;
    
        SynchronizedSet(Set<E> s) {
            super(s);
        }
        SynchronizedSet(Set<E> s, Object mutex) {
            super(s, mutex);
        }
    
        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return c.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return c.hashCode();}
        }
    }
    

  3. It extends SynchronizedCollection, which has the following interesting methods next to the obvious ones:

    // 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!
    }
    

The mutex used here is the same object as to which all operations of Collections.synchronizedSet lock to.

Now we can, judging by the implementation say that it is thread safe to use Collections.synchronizedSet(...).forEach(...), but is it also thread safe by specification?

(Confusingly enough, Collections.synchronizedSet(...).stream().forEach(...) is not thread safe by implementation, and the verdict of the specification seems to be unknown aswell.)

解决方案

As you wrote, judging by implementation, forEach() is thread-safe for the collections provided with JDK (see disclaimer below) as it requires monitor of mutex to be acquired to proceed.

My opinion - no, and here is an explanation. Collections.synchronizedXXX() javadoc, rewritten in short words, says - "all methods are thread-safe except for those used for iterating over it".

My other, although very subjective argument is what yshavit wrote - unless told/read that, consider API/class/whatever not thread-safe.

Now, let's take a closer look at the javadocs. I guess I may state that method forEach() is used to iterate over it, so, following the advice from javadoc, we should consider it not thread-safe, although it is opposite to reality (implementation).

Anyway, I agree with yshavit's statement that the documentation should be updated as this is most likely a documentation, not implementation flaw. But, no one can say for sure except for JDK developers, see concerns below.

The last point I'd like to mention within this discussion - we can assume that custom collection can be wrapped with Collections.synchronizedXXX(), and the implementation of forEach() of this collection can be... can be anything. The collection might perform asynchronous processing of elements within the forEach() method, spawn a thread for each element... it is bounded only by author's imagination, and synchronized(mutex) wrap cannot guarantee thread-safety for such cases. That particular issue might be the reason not to declare forEach() method as thread-safe..

这篇关于是迭代通过Collections.synchronizedSet(...)。forEach()保证线程安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 01:59