6

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

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 个线程同时调用“remove”和“contains”方法,是否会发生异常?有很多线程可以访问单例,所以我想知道在性能影响最小的情况下执行此操作的方法。

4

2 回答 2

15

该文档回答了您的问题。

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

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

至于containsand remove,您不必手动同步。我正在查看的源代码,Collections它看起来像是为你做的:

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

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

于 2013-07-10T18:34:29.913 回答
6

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

您也可以通过返回列表的副本而不是getPlayers. (如果您正在使用Collections.synchronizedList

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

这具有在添加/删除完成后列表过期的副作用

编辑: 窃取 Grzegorz 的建议unmodifiableList

于 2013-07-10T18:38:30.067 回答