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

我有一个multithreading应用程序,它有一个只有主线程更新(写入)的centrlaised列表。 然后我有几个其他线程需要定期检索当前状态的列表。 有没有一种方法可以让我这样做?

这取决于您希望如何限制并发性。 最简单的方法可能是使用CopyOnWriteArrayList 。 从中获取迭代器时,该迭代器将镜像列表在创建迭代器时的时间点迭代器不会看到后续修改。 好处是它可以应对相当多的争用,缺点是添加新物品相当昂贵。

另一种方法是锁定,最简单的方法可能是使用Collections.synchronizedList包装列表并在迭代时在列表上进行同步。

第三种方法是使用某种BlockingQueue并将新元素提供给worker。

编辑:由于OP声明只需要一个快照,因此CopyOnWriteArrayList可能是最好的开箱即用选择。 另一种选择(更便宜的添加,但更昂贵的读取)只是在需要traversion时创建synchronizedList的副本(copy-on-read而不是copy-on-write):

 List originalList = Collections.synchronizedList(new ArrayList()); public void mainThread() { while(true) originalList.add(getSomething()); } public void workerThread() { while(true) { List copiedList; synchronized (originalList) { copiedList = originalList.add(something); } for (Foo f : copiedList) process(f); } } 

编辑:想想看,复制读取版本可以简化一点,以避免所有synchronized块:

 List 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:这是一个简单的包装,用于复制读取列表,它不使用任何帮助程序,并试图在锁定中尽可能细粒度(我故意使它有些过分,接近于次优,以certificate需要锁定的地方):

 class CopyOnReadList { private final List items = new ArrayList(); public void add(T item) { synchronized (items) { // Add item while holding the lock. items.add(item); } } public List makeSnapshot() { List copy = new ArrayList(); synchronized (items) { // Make a copy while holding the lock. for (T t : items) copy.add(t); } return copy; } } // Usage: CopyOnReadList stuff = new CopyOnReadList(); stuff.add("hello"); for (String s : stuff.makeSnapshot()) System.out.println(s); 

基本上,当你在以下时锁定:

  1. …将项目添加到列表中。
  2. …遍历列表以制作它的副本。

您可以考虑使用读写锁机制。 如果JDK版本为1.5或更高版本,则可以使用ReentrantReadWriteLock 。

看看Java的Concurrency Package 。 应该有一些你可以使用的东西。

子线程是否需要只读访问权限? 只需要列表顶部的项目吗? 如何使用列表,可能有助于我们更好地理解您的问题,并指出您更明确的方向。

要传递列表的快照,只需创建一个由原始列表填充的新列表。

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

同步在这里可能有益也可能没有益处。 我不确定。

如果线程只是“读取”列表,那么只需使用List即可。 如果列表将“写入”(以任何方式更改),请使用Vector而不是List。

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

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

如果写入不频繁且数据大小很小,则CopyOnWriteArrayList是一个很好的选择。 否则,您的写入性能可能会成为问题。

如果您不需要完整的List接口(主要是通过索引的随机访问),那么您有更多选择。 如果你的用例满足Queue接口,那么像ConcurrentLinkedQueue这样的东西将是一个很好的选择。 如果您可以使用Queue界面,那么这样的事情就变得可能:

 Queue originalList = new ConcurrentLinkedQueue(); 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); } }