【问题标题】:java concurrent Array List accessjava并发数组列表访问
【发布时间】:2013-07-10 18:32:35
【问题描述】:

我有一个单例对象。该对象声明:

List<Player> players = new ArrayList<Player>();

同一个对象还对这个arrayList指定了4个操作:

public List<Player> getPlayers() {
return players;
} // the result of this method can be used in another object as an iterator (or maybe by index-access)

public void removePlayer(Player player) {
players.remove(player);
}

public void addPlayer(Player player) {
players.add(player);
}

public boolean isPresent(Player player) {
if (players.constans(player)) {...
}

现在在构造函数中我是这样做的:

players = Collections.synchronizedList(new ArrayList<Player>());

但是同步这些方法的正确方法是什么。好像如果我在另一个类中使用迭代器,它仍然会通过并发修改异常。如果 2 个线程同时调用“删除”和“包含”方法,是否会发生异常?有很多线程可以访问单例,所以我想知道在性能影响最小的情况下执行此操作的方法。

【问题讨论】:

  • 正如 tieTYT 所解释的,对列表的每次访问都必须同步。这是一个乌托邦的目标。因此,您不应该让任何人从您的班级之外访问该列表。或者你应该使用并发列表。
  • 并发使用“contains”和“remove”会不会抛出异常?
  • 如果可以接受,您可以返回列表的副本,甚至可以返回不可修改的列表副本,以确保任何更改都会失败并出现异常。对于那些认为他们可以在不使用您的 API 的情况下添加对象的人。
  • 否,因为这些方法是同步的。但是,一旦您进行了迭代,或者执行了诸如if (!list.contains(foo)) list.add(foo) 之类的check-then-act 操作,您就需要显式同步。还要注意隐藏的迭代,例如new ArrayList&lt;&gt;(list)

标签: java list collections concurrency


【解决方案1】:

The documentation answers your question.

用户在迭代返回的列表时必须手动同步它:

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized(list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

至于containsremove,您不必手动同步。我正在查看Collections 的源代码,它看起来对你有用:

    public boolean contains(Object o) {
        synchronized (mutex) {return c.contains(o);}
    }
    public boolean remove(Object o) {
        synchronized (mutex) {return c.remove(o);}
    }

如果您必须自己做这些事情,这将不是一个同步列表。

【讨论】:

  • 请原谅我的无知,那么“Collections.synchronizedList”的附加值是什么?如果您在原始列表上进行同步,那么这是否解决了所有问题?
  • @TimCooper 在文档中声明为Iterator i = list.iterator(); // Must be in synchronized block。是的,我对你的问题 +1,这听起来合乎逻辑,synchronizedList 有望完成这项工作
【解决方案2】:

如果您不打算更新它,请经常使用CopyOnWriteArrayList,否则@tieTYT 的建议有效。

您也可以通过在getPlayers 中返回列表副本而不是实际列表来自行管理。 (如果您使用的是Collections.synchronizedList

public List<Player> getPlayers(){
    synchronized(list){
       Collections.unmodifiableList(new ArrayList<Player>(list));
    }
}

这会导致列表在添加/删除完成后过期

编辑: 窃取 Grzegorz 对unmodifiableList的建议

【讨论】:

  • 反正同步之后不会过期吗?
猜你喜欢
  • 2015-01-02
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-12
  • 2013-04-09
  • 1970-01-01
相关资源
最近更新 更多