簡體   English   中英

JAVA:用於訪問java中列表的並發控制

[英]JAVA: Concurrency control for access to list in java

我有一個多線程應用程序,它有一個只有主線程更新(寫入)的centrlaised列表。 然后我有幾個其他線程需要定期檢索當前狀態的列表。 有沒有一種方法可以讓我這樣做?

這取決於您希望如何限制並發性。 最簡單的方法可能是使用CopyOnWriteArrayList 從中獲取迭代器時,該迭代器將鏡像列表查看迭代器創建時的時間點 - 后續修改對迭代器不可見。 好處是它可以應對相當多的爭用,缺點是添加新物品相當昂貴。

另一種方法是鎖定,最簡單的方法可能是使用Collections.synchronizedList包裝列表並在迭代時在列表上進行同步。

第三種方法是使用某種BlockingQueue並將新元素提供給worker。

編輯:由於OP聲明只需要一個快照,因此CopyOnWriteArrayList可能是最好的開箱即用選擇。 另一種選擇(更便宜的添加,但更昂貴的讀取)只是在需要traversion時創建synchronizedList的副本(copy-on-read而不是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塊:

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);
    }
}

編輯2:這是一個簡單的包裝,用於讀取副本,不使用任何助手,並試圖在鎖定中盡可能細粒度(我故意使它有些過分,接近於次優,以證明需要鎖定的地方):

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. ...遍歷列表以制作它的副本。

您可以考慮使用讀寫鎖機制。 如果您的JDK版本是1.5或更高版本,則可以使用ReentrantReadWriteLock

看看Java的Concurrency Package 應該有一些你可以使用的東西。

子線程是否需要只讀訪問權限? 只需要列表頂部的項目嗎? 如何使用列表,可能有助於我們更好地理解您的問題,並指出您更明確的方向。

要傳遞列表的快照,只需創建一個由原始列表填充的新列表。

List<Object> newList;
synchronize (originalList)
{
    newList = new ArrayList<Object>(originalList);
}
return newList;

同步在這里可能有益也可能沒有益處。 我不確定。

如果線程只是“讀取”列表,那么只需使用List即可。 如果列表將“寫入”(以任何方式更改),請使用Vector而不是List。

如果您只是想要一個只讀快照而且列表不是太大:

private static List<TypeHere> getCurrentList() { 
  /* code to return the list as it currently is */
}
public static List<TypeHere> snapshot() {
  TypeHere[] objects = getCurrentList().toArray(new TypeHere[] {});
  return Collections.unmodifiableList(Arrays.asList(objects));
} 

如果寫入不頻繁且數據大小很小,則CopyOnWriteArrayList是一個很好的選擇。 否則,您的寫入性能可能會成為問題。

如果您不需要完整的List接口(主要是通過索引的隨機訪問),那么您有更多選項。 如果你的用例滿足Queue接口,那么像ConcurrentLinkedQueue這樣的東西將是一個很好的選擇。 如果您可以使用Queue界面,那么這樣的事情就變得可能:

Queue<Foo> originalList = new ConcurrentLinkedQueue<Foo>();

public void mainWrite() {
    // main thread
    originalList.add(getSomething()); // no locking needed
}

public void workerRead() {
    // executed by worker threads
    // iterate without holding the lock
    for (Foo f: originalList) {
        process(f);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM