集合上的线程安全迭代

发布于 2024-10-09 01:49:18 字数 430 浏览 0 评论 0原文

我们都知道,当使用Collections.synchronizedXXX(例如synchronizedSet())时,我们会获得底层集合的同步“视图”。

然而,这些包装器生成方法的文档指出,当使用迭代器迭代集合时,我们必须在集合上显式同步

您选择哪个选项来解决这个问题?

我只能看到以下方法:

  1. 按照文档所述进行操作: 在集合上同步
  2. 在调用 iterator() 之前克隆集合
  3. 使用迭代器是线程安全的集合(我只知道 < code>CopyOnWriteArrayList/Set)

还有一个额外的问题:当使用同步视图时 - foreach/Iterable 的使用是线程安全的吗?

We all know when using Collections.synchronizedXXX (e.g. synchronizedSet()) we get a synchronized "view" of the underlying collection.

However, the document of these wrapper generation methods states that we have to explicitly synchronize on the collection when iterating of the collections using an iterator.

Which option do you choose to solve this problem?

I can only see the following approaches:

  1. Do it as the documentation states: synchronize on the collection
  2. Clone the collection before calling iterator()
  3. Use a collection which iterator is thread-safe (I am only aware of CopyOnWriteArrayList/Set)

And as a bonus question: when using a synchronized view - is the use of foreach/Iterable thread-safe?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

随梦而飞# 2024-10-16 01:49:18

您已经真正回答了您的额外问题:不,使用增强的 for 循环安全 - 因为它使用迭代器。

至于哪种方法是最合适的 - 这实际上取决于您的上下文:

  • 写入是否非常罕见?如果是这样,CopyOnWriteArrayList 可能是最合适的。
  • 集合是否相当小并且迭代速度很快? (即您在循环中没有做太多工作)如果是这样,同步可能会很好 - 特别是如果这种情况不会经常发生(即您不会对集合有太多争用)。
  • 如果您正在做大量工作并且不想阻止同时工作的其他线程,那么克隆集合的命中率很可能是可以接受的。

You've already answered your bonus question really: no, using an enhanced for loop isn't safe - because it uses an iterator.

As for which is the most appropriate approach - it really depends on how your context:

  • Are writes very infrequent? If so, CopyOnWriteArrayList may be most appropriate.
  • Is the collection reasonably small, and the iteration quick? (i.e. you're not doing much work in the loop) If so, synchronizing may well be fine - especially if this doesn't happen too often (i.e. you won't have much contention for the collection).
  • If you're doing a lot of work and don't want to block other threads working at the same time, the hit of cloning the collection may well be acceptable.
╰つ倒转 2024-10-16 01:49:18

取决于您的访问模型。如果并发量低,写入频繁,1的性能最好。如果并发度高且写入不频繁,则 3 性能最佳。选项 2 几乎在所有情况下都表现不佳。

foreach 调用 iterator(),因此完全相同的事情也适用。

Depends on your access model. If you have low concurrency and frequent writes, 1 will have the best performance. If you have high concurrency with and infrequent writes, 3 will have the best performance. Option 2 is going to perform badly in almost all cases.

foreach calls iterator(), so exactly the same things apply.

幻梦 2024-10-16 01:49:18

您可以使用 Java 5.0 中添加的新集合之一,它支持迭代时的并发访问。另一种方法是使用线程安全的 toArray 进行复制(在复制期间)。

Collection<String> words = ...
// enhanced for loop over an array.
for(String word: words.toArray(new String[0])) {

}

You could use one of the newer collections added in Java 5.0 which support concurrent access while iterating. Another approach is to take a copy using toArray which is thread safe (during the copy).

Collection<String> words = ...
// enhanced for loop over an array.
for(String word: words.toArray(new String[0])) {

}
彼岸花ソ最美的依靠 2024-10-16 01:49:18

我可能完全不同意您的要求,但如果您不了解这些要求,请查看 google-考虑到“支持不变性”的集合。

I might be totally off with your requirements, but if you are not aware of them, check out google-collections with "Favor immutability" in mind.

权谋诡计 2024-10-16 01:49:18

我建议删除Collections.synchronizedXXX并在客户端代码中统一处理所有锁定。基本集合不支持线程代码中有用的复合操作,即使您使用 java.util.concurrent.* 代码也会更加困难。我建议尽可能多地保留与线程无关的代码。将困难且容易出错的线程安全(如果我们很幸运)代码保持在最低限度。

I suggest dropping Collections.synchronizedXXX and handle all locking uniformly in the client code. The basic collections don't support the sort of compound operations useful in threaded code, and even if you use java.util.concurrent.* the code is more difficult. I suggest keeping as much code as possible thread-agnostic. Keep difficult and error-prone thread-safe (if we are very lucky) code to a minimum.

深陷 2024-10-16 01:49:18

您的所有三个选项都将起作用。选择适合您情况的产品取决于您的情况。

如果您想要一个列表实现并且不介意每次写入时都会复制底层存储,则 CopyOnWriteArrayList 将起作用。只要您没有很大的集合,这对于性能来说就非常好。

如果您需要 Map,则 ConcurrentHashMap 或“ConcurrentHashSet”(使用 Collections.newSetFromMap)可以使用设置接口,显然这样你无法获得随机访问。太棒了!这两者的特点是它们可以很好地处理大型数据集——当它们发生变化时,它们只是复制底层数据存储的一小部分。

All three of your options will work. Choosing the right one for your situation will depend on what your situation is.

CopyOnWriteArrayList will work if you want a list implementation and you don't mind the underlying storage being copied every time you write. This is pretty good for performance as long as you don't have very big collections.

ConcurrentHashMap or "ConcurrentHashSet" (using Collections.newSetFromMap) will work if you need a Map or Set interface, obviously you don't get random access this way. One great! thing about these two is that they will work well with large data sets - when mutated they just copy little bits of the underlying data storage.

戒ㄋ 2024-10-16 01:49:18

它确实取决于实现克隆/复制/toArray()、new ArrayList(..) 等所需的结果,获得快照并且不锁定集合。
使用synchronized(collection)并通过确保迭代结束时的迭代将不会进行任何修改,即有效地锁定它。

旁注:(当内部需要创建临时 ArrayList 时,通常首选 toArray(),但有一些例外)。另请注意,除了 toArray() 之外的任何内容都应该包装在使用 Collections.synchronizedXXX 提供的同步(集合)中。

It does depend on the result one needs to achieve cloning/copying/toArray(), new ArrayList(..) and the likes obtain a snapshot and does not lock the the collection.
Using synchronized(collection) and iteration through ensure by the end of the iteration would be no modification, i.e. effectively locking it.

side note:(toArray() is usually preferred with some exceptions when internally it needs to create a temporary ArrayList). Also please note, anything but toArray() should be wrapped in synchronized(collection) as well, provided using Collections.synchronizedXXX.

舟遥客 2024-10-16 01:49:18

这个问题相当旧(抱歉,我有点晚了..)但我仍然想添加我的答案。

我会选择你的第二个选择(即在调用 iterator() 之前克隆集合),但有一个重大的转变。

假设,您想使用迭代器进行迭代,则不必在调用 .iterator() 之前复制 Collection 并否定(我松散地使用术语“否定”)迭代器模式的想法,但您可以编写一个“线程安全迭代器”。

它会在相同的前提下工作,复制 Collection,但不让迭代类知道您就是这样做的。这样的迭代器可能如下所示:

class ThreadSafeIterator<T> implements Iterator<T> {
    private final Queue<T> clients;
    private T currentElement;
    private final Collection<T> source;

    AsynchronousIterator(final Collection<T> collection) {
        clients = new LinkedList<>(collection);
        this.source = collection;
    }

    @Override
    public boolean hasNext() {
        return clients.peek() != null;
    }

    @Override
    public T next() {
        currentElement = clients.poll();
        return currentElement;
    }

    @Override
    public void remove() {
        synchronized(source) {
            source.remove(currentElement);
        }
    }
}

更进一步,您可以使用信号量类来确保线程安全或其他东西。但对删除方法持保留态度。

关键是,通过使用这样的迭代器,任何人,无论是迭代还是被迭代的类(这是一个真正的词)都不必担心线程安全。

This Question is rather old (sorry, i am a bit late..) but i still want to add my Answer.

I would choose your second choice (i.e. Clone the collection before calling iterator()) but with a major twist.

Asuming, you want to iterate using iterator, you do not have to coppy the Collection before calling .iterator() and sort of negating (i am using the term "negating" loosely) the idea of the iterator pattern, but you could write a "ThreadSafeIterator".

It would work on the same premise, coppying the Collection, but without letting the iterating class know, that you did just that. Such an Iterator might look like this:

class ThreadSafeIterator<T> implements Iterator<T> {
    private final Queue<T> clients;
    private T currentElement;
    private final Collection<T> source;

    AsynchronousIterator(final Collection<T> collection) {
        clients = new LinkedList<>(collection);
        this.source = collection;
    }

    @Override
    public boolean hasNext() {
        return clients.peek() != null;
    }

    @Override
    public T next() {
        currentElement = clients.poll();
        return currentElement;
    }

    @Override
    public void remove() {
        synchronized(source) {
            source.remove(currentElement);
        }
    }
}

Taking this a Step furhter, you might use the Semaphore Class to ensure thread-safety or something. But take the remove method with a grain of salt.

The point is, by using such an Iterator, no one, neither the iterating nor the iterated Class (is that a real word) has to worrie about Thread safety.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文