2017-08-28 8 views
0

私の理解しているように、ConcurrentHashMapは、同時ハッシュマップ例外を取得することなく、複数のスレッドが同じハッシュマップ上で読み書きできるようにします(追加/削除)。同時ハッシュマップの同期ブロックを使用していますか?

私は4つのスレッドを持ち、各スレッドはハッシュマップを更新できます。私は、現在のスレッドがそれを更新している間に他のスレッドがハッシュマップに書き込み/更新しないようにします。

ConcurrentHashMap<String, Integer> playerLoginCounterHashMap = new ConcurrentHashMap<>(); 

    ExecutorService executorService = Executors.newFixedThreadPool(4); 

    for (int i = 0; i < 4; i++) { 

     executorService.submit(new Runnable() { 
      @Override 
      public void run() { 
       synchronized (playerLoginCounterHashMap) { 
        if (playerLoginCounterHashMap.get("testPlayer") == null) { 
         playerLoginCounterHashMap.put("testPlayer", 1); 
        } else { 
         playerLoginCounterHashMap.put("testPlayer", playerLoginCounterHashMap.get("testPlayer").intValue() + 1); 
        } 
       } 
      } 
     }); 
    } 

これは正しい方法ですか?同期ブロックがなければ、正しくない値が得られます。

+0

そのコードによって例外が生成されますか? –

+2

並行処理を可能にするには、 'putIfAbsent'や' computeIfAbsent'のような同時認識APIを使う必要があります。それ以外の場合、更新プロセス全体をアトミックにする必要がある場合、唯一の解決方法は、ロックを取って、更新中に他のスレッドがマップを使用しないようにすることです。その時点でConcurrentHashMapは値を追加しません。 –

+0

@DanielPryden putIfAbsentは、docからのcomputeIfAbsentのようなものではないようですが、 –

答えて

2

はい、正しいです(マップが更新される唯一の場所であると仮定します)。マップの固有の非ブロッキング並行性に頼るのではなく、同期するため非効率です。

あなたが代わりにcompute()を使用する必要があります。

playerLoginCounterHashMap.compute(
    "testPlayer", 
    (key, value) -> value == null ? 1 : value + 1); 

またはmerge()を:

playerLoginCounterHashMap.merge(
    "testPlayer", 
    1, 
    Integer::sum); 
1

てください、ユーザごとの長いカウンターを保存する簡単な場合には、それはGoogle Guava AtomicLongMapを使用するために意味をなすかもしれないことに注意してください:

final AtomicLongMap<String> loginCounterByPlayerName = AtomicLongMap.create(); 
final ExecutorService executorService = Executors.newFixedThreadPool(4); 
for (int i = 0; i < 4; i++) { 
    executorService.submit(new Runnable() { 
     @Override 
     public void run() { 
      loginCounterByPlayerName.addAndGet("testPlayer", 1); 
     } 
    }); 
} 

問題はカウンタが0から始まることです。

関連する問題