[英]Are values of ConcurrentHashMap being synchronized by default?
下面是我在这里找到的一段代码。 它模仿一个在线游戏服务器,玩家可以在其中加入桌子。
public class GameServer {
public Map<String, List<Player>> tables = new ConcurrentHashMap<String, List<Player>>();
public void join(Player player, Table table) {
if (player.getAccountBalance() > table.getLimit()) {
List<Player> tablePlayers = tables.get(table.getId());
synchronized (tablePlayers) {
if (tablePlayers.size() < 9) {
tablePlayers.add(player);
}
}
}
}
public void leave(Player player, Table table) {/*Method body skipped for brevity*/}
public void createTable() {
Table table = new Table();
tables.put(table.getId(), table);
}
public void destroyTable(Table table) {
tables.remove(table.getId());
}
}
tablePlayers是ConcurrentHashMap的值之一。
List<Player> tablePlayers = tables.get(table.getId());
既然ConcurrentHashMap已经是线程安全的,为什么要使用它时还需要同步其value对象?
synchronized (tablePlayers) {
...
}
因为在程序中使用线程安全的对象不会使程序成为线程安全的。
有问题的代码在这里:
synchronized (tablePlayers) {
if (tablePlayers.size() < 9) {
tablePlayers.add(player);
}
}
显然,打算将桌子的大小限制为不超过9个玩家。
没有synchronized
就无法工作。 调用tablePlayers.add(player)
时,将无法知道表的大小。 问题是,线程A可以调用tablePlayers.size()
,并获取数字8。然后线程B可以将玩家添加到表中。 线程A可以测试8 < 9
(在我看来不错),然后向表中添加另一个玩家。 结果是桌子上有十个玩家。 不是作者的意图。
该表是“线程安全的”这一事实仅意味着该表自身的内部数据不会在多个线程访问它时被破坏。 这并不意味着该方案将永远做正确的事与数据。
既然ConcurrentHashMap已经是线程安全的,为什么要使用它时还需要同步其value对象?
CHM 是线程安全的,但这仅意味着它可以保护自己免受并行操作的影响。 仅仅因为您将对象存储在CHM中并不能使该对象也具有神奇的线程安全性。 从CHM中检索表列表后,如果要由多个线程访问该对象,则可以适当地锁定该对象。
就您的代码而言,如果两个线程同时调用join,会发生什么?
synchronized (tablePlayers) {
// race condition here because a test...
if (tablePlayers.size() < 9) {
// and then the add method
tablePlayers.add(player);
}
}
如果没有synchronized
块,则这两个线程可能都同时检查,并且看到表的大小为8(例如),然后两个都将自己添加到表中。 这就是比赛条件的全部。 因为有2个操作(一个测试,然后附加)时, synchronized
是必要的,以确保只有1在一个时间的线程可以看到,如果它们能够加入一个表,然后加入。
另外, synchronized
块还保护表列表本身,而表列表本身可能不是synchronized
集合。 如果没有synchronized
块,则这两个线程可以同时写入List
并破坏List
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.