且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

JAVA:用于访问Java中列表的并发控制

更新时间:2023-01-15 17:31:58

这取决于您如何限制并发性.最简单的方法可能是使用 CopyOnWriteArrayList 一个>.当您从中获取迭代器时,该迭代器将反映列表在创建迭代器时的外观-迭代器将看不到后续修改.好处是它可以应付很多争用,缺点是添加新项相当昂贵.

That depends on how you want to restrict the concurrency. The easiest way is probably using CopyOnWriteArrayList. When you grab an iterator from it, that iterator will mirror how the list looked at the point in time when the iterator was created - subsequent modifications will not be visible to the iterator. The upside is that it can cope with quite a lot of contention, the drawback is that adding new items is rather expensive.

另一种方法是锁定,最简单的方法可能是用

The other way of doing is locking, the simplest way is probably wrapping the list with Collections.synchronizedList and synchronizing on the list when iterating.

第三种方法是使用某种 BlockingQueue 并将新元素提供给工作人员.

A third way is using some kind of BlockingQueue and feed the new elements to the workers.

编辑:由于OP声明仅需要快照,因此CopyOnWriteArrayList可能是***的即用型替代方案.另一种方法(添加成本较低,但阅读成本较高)是仅在需要遍历时创建synchronizedList的副本(读取时复制而不是写入时复制):

As the OP stated only a snapshot is needed, CopyOnWriteArrayList is probably the best out-of-the-box alternative. An alternative (for cheaper adding, but costlier reading) is just creating a copy of a synchronizedList when traversion is needed (copy-on-read rather than copy-on-write):

List<Foo> originalList = Collections.synchronizedList(new ArrayList());

public void mainThread() {
    while(true)
        originalList.add(getSomething());
}

public void workerThread() {
    while(true) {
        List<Foo> copiedList;
        synchronized (originalList) {
             copiedList = originalList.add(something);
        }
        for (Foo f : copiedList) process(f);
    }
}

考虑一下,读取时复制的版本可以简化一点,以避免所有synchronized块:

Come to think of it, the copy-on-read version could simplified a bit to avoid all synchronized blocks:

List<Foo> originalList = Collections.synchronizedList(new ArrayList());

public void mainThread() {
    while(true)
        originalList.add(getSomething());
}

public void workerThread() {
    while(true) {
        for (Foo f : originalList.toArray(new Foo[0])) 
            process(f);
    }
}

以下是一个简单的包装,用于读取时复制列表,该包装不使用任何帮助程序,并试图尽可能地细化锁定(我ve故意使它有些过分,接近次优,以表明需要锁定的地方):

Edit 2: Here's a simple wrapper for a copy-on-read list which doesn't use any helpers, and which tries to be as fine-grained in the locking as possible (I've deliberately made it somewhat excessive, bordering on suboptimal, to demonstrate where locking is needed):

class CopyOnReadList<T> {

    private final List<T> items = new ArrayList<T>();

    public void add(T item) {
        synchronized (items) {
            // Add item while holding the lock.
            items.add(item);
        }
    }

    public List<T> makeSnapshot() {
        List<T> copy = new ArrayList<T>();
        synchronized (items) {
            // Make a copy while holding the lock.
            for (T t : items) copy.add(t);
        }
        return copy;
    }

}

// Usage:
CopyOnReadList<String> stuff = new CopyOnReadList<String>();
stuff.add("hello");
for (String s : stuff.makeSnapshot())
    System.out.println(s);

基本上,当您锁定时:

  1. ...在列表中添加一个项目.
  2. ...遍历列表以制作副本.