我認爲你正在尋找一種列表的雙緩衝的。
我已經測試過這與多個生產者和多個消費者,它似乎很好地工作。
基本上,你需要保存一個列表,當被請求時,被一個新的空列表所取代。在交換時正確處理線程會增加一點複雜性。這可以處理添加到列表中的多個線程以及多個線程獲取迭代列表。
請注意,您的架構略有變化(從列表中一次拉一個條目)意味着您可以使用BlockingQueue
這可能是一個更好的解決方案。
public class DoubleBufferedList<T> {
// Atomic reference so I can atomically swap it through.
// Mark = true means I am adding to it so unavailable for iteration.
private AtomicMarkableReference<List<T>> list = new AtomicMarkableReference<List<T>>(newList(), false);
// Factory method to create a new list - may be best to abstract this.
protected List<T> newList() {
return new ArrayList<T>();
}
// Get and replace the current list.
public List<T> getList() {
// Atomically grab and replace the list with an empty one.
List<T> empty = newList();
List<T> it;
// Replace an unmarked list with an empty one.
if (!list.compareAndSet(it = list.getReference(), empty, false, false)) {
// Failed to replace!
// It is probably marked as being appended to but may have been replaced by another thread.
// Return empty and come back again soon.
return Collections.EMPTY_LIST;
}
// Successfull replaced an unmarked list with an empty list!
return it;
}
// Add an entry to the list.
public void addToList(T entry) {
List<T> it;
// Spin on get and mark.
while (!list.compareAndSet(it = list.getReference(), it, false, true)) {
// Spin on mark.
}
// Successfully marked! Add my new entry.
it.add(entry);
// Unmark it. Should never fail because once marked it will not be replaced.
if (!list.attemptMark(it, false)) {
throw new IllegalMonitorStateException("it changed while we were adding to it!");
}
}
}
爲什麼你堅持'for-each'循環而不是'for'與索引? – 2013-02-15 12:18:14