ConcurrentHashMap的值是否默认同步?

时间:2017-03-24 00:59:35

标签: java multithreading concurrency concurrenthashmap

以下是我发现here的一段代码。 它模仿在线游戏服务器,玩家可以加入桌面。

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已经是线程安全的,为什么我们仍然需要在我们想要使用它时同步它的值对象?

  synchronized (tablePlayers) {
    ...
  }

2 个答案:

答案 0 :(得分:0)

因为在程序中使用线程安全对象不会使程序成为线程安全的。

有问题的代码在这里:

  synchronized (tablePlayers) {
    if (tablePlayers.size() < 9) {
      tablePlayers.add(player);
    }
  }

显然,它希望将桌子的大小限制为不超过9名玩家。

没有synchronized,它就无法工作。调用tablePlayers.add(player)时无法知道表的大小。问题是,线程A可以调用tablePlayers.size(),然后返回数字8.然后线程B可以将一个玩家添加到表中。线程A可以测试8 < 9(看起来对我好),并将另一个玩家添加到表中。结果将是表中的十名玩家。不是作者的意图。

表是&#34;线程安全&#34;仅表示当多个线程访问表时,表的内部数据不会被破坏。 意味着程序将始终使用数据做正确的事情。

答案 1 :(得分:0)

  

现在ConcurrentHashMap已经是线程安全的,为什么我们还想在使用它时同步它的值对象呢?

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块,则2个线程可能同时检查并看到表的大小为8(例如),然后两者都将自己添加到表中。这就是race condition的全部意义所在。因为有2个操作(测试然后是添加),所以synchronized是必要的,以确保一次只有1个线程可以看到他们是否可以加入表然后加入它

此外,synchronized块还保护表列表本身,这可能不是synchronized集合本身。如果没有synchronized块,那么2个线程可以同时写入List并破坏它。

相关问题