[英]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);
基本上,當你在以下時鎖定:
您可以考慮使用讀寫鎖機制。 如果您的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.