2017-03-24 10 views
0

以下はコードhereのコードです。 プレイヤーがテーブルに参加できるオンラインゲームサーバーを模倣しています。ConcurrentHashMapの値はデフォルトで同期していますか?

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の値の1つです。

List<Player> tablePlayers = tables.get(table.getId()); 

ConcurrentHashMapはすでにスレッドセーフです。なぜ、それを使用するときに値オブジェクトを同期させる必要があるのですか?

synchronized (tablePlayers) { 
    ... 
    } 
+0

これは2つの全く異なる質問です。 1つにこだわる。 – shmosel

+4

* ConcurrentHashMapはすでにスレッドセーフです。なぜ、それを使用するには値オブジェクトを同期させる必要がありますか?**あなたは** **を使用していません。そこから値**を取得**しています。 ConcurrentHashMapはその値を保護せず、参照への*アクセス*を制御するだけです。参照を取得したら、安全に使うことはあなた次第です。 – shmosel

答えて

0

プログラムでスレッドセーフなオブジェクトを使用しても、スレッドセーフではありません。

問題のコードはここにある:

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

もちろん、せいぜい9人の選手にテーブルのサイズを制限するintentedされます。

​​なしでは機能しません。 tablePlayers.add(player)が呼び出されたときにテーブルのサイズを知る方法はありません。問題は、スレッドAがtablePlayers.size()を呼び出して番号8を返すことができるということです。スレッドBはプレーヤーをテーブルに追加できます。スレッドAは8 < 9(OKと思われます)をテストし、別のプレーヤーをテーブルに追加することができます。その結果、テーブルには10人のプレイヤーがいます。著者が意図したものではありません。

テーブルが「スレッドセーフ」であるという事実は、複数のスレッドがテーブルにアクセスするときにテーブルの内部データが破損しないことを意味します。 ではなく、は、プログラムが常にデータで正しいことをすることを意味します。

0

ConcurrentHashMapはすでにスレッドセーフです。なぜ、値オブジェクトを使用するときに値のオブジェクトを同期させる必要があるのですか?

CHM スレッドセーフであるが、それは、それが同時操作から自体を保護することを意味します。 CHMにオブジェクトを格納しているという理由だけでも、オブジェクトはスレッドセーフではありません。 CHMからテーブルリストを取得すると、複数のスレッドがアクセスする場合、オブジェクトを適切にロックする必要があります。

コードの場合、2つのスレッドが同時にjoinを呼び出すとどうなりますか?

synchronized (tablePlayers) { 
    // race condition here because a test... 
    if (tablePlayers.size() < 9) { 
     // and then the add method 
     tablePlayers.add(player); 
    } 
} 

​​ブロックがなかった場合は、2つのスレッドが両方同時にチェックして、テーブルのサイズは、(例えば)8だったし、その後の両方がテーブルに自分自身を追加するために行くことに表示される場合があります。これはrace conditionのすべてです。 2つの操作(テストと追加)があるため、​​は、一度に1つのスレッドだけがテーブルに参加できるかどうかを確認するために必要です。が参加します。

また、​​ブロックは、おそらく​​コレクション自体ではないテーブルリスト自体を保護します。​​ブロックがなければ、2つのスレッドは同時にListに書き込んで破損する可能性があります。

関連する問題