2

ConcurrentHashMapアトミックに複数の操作を実行する方法を見つけようとしています。

私の論理は次のようなものです:

if (!map.contains(key)) {
    map.put(key, value);

    doSomethingElse();
}

方法があることを知りましたputIfAbsentdoSomethingElseしかし、それを使用しても、アトミックに呼び出すことはできません。

同期/クライアント側のロックに頼る以外に、そのようなことを行う方法はありますか?

それが役立つ場合doSomethingElse、私の場合はかなり複雑になり、マップに追加したばかりのキーを探すスレッドを作成して開始する必要があります。

4

4 に答える 4

5

それが役に立てば、私の場合の doSomethingElse は非常に複雑で、マップに追加したばかりのキーを探すスレッドを作成して開始する必要があります。

その場合は、通常、外部で同期する必要があります。

状況によっては (doSomethingElse()マップの状態がどのようなものであるか、および他のスレッドがマップに対して何を行うかによって異なります)、次の方法も機能する場合があります。

if (map.putIfAbsent(key, value) == null) {
    doSomethingElse();
}

doSomethingElse()これにより、特定のキーに対して1 つのスレッドのみが使用されるようになります。

于 2013-03-19T13:41:29.090 に答える
3

これは、最初に成功したスレッドがマップに配置されるまですべての配置スレッドを待機させない限り、機能します。

if(map.get(key) == null){

  Object ret = map.putIfAbsent(key,value);
  if(ret == null){ // I won the put
     doSomethingElse();
  }
}

これで、多くのスレッドが同じスレッドを配置している場合、key1つだけが勝ち、1つだけが勝ちますdoSomethingElse()

于 2013-03-19T13:58:49.763 に答える
2

エントリごとにロックを保持する場合があります。これにより、2 つのスレッドが同じ要素にアクセスしようとしない限り、ロックなしの同時更新が可能になります。

class LockedReference<T> {
  Lock lock = new ReentrantLock();;
  T value;
  LockedReference(T value) {this.value=value;}      
}

LockedReference<T> ref = new LockedReference(value);
ref.lock.lock(); //lock on the new reference, there is no contention here
try {
  if (map.putIfAbsent(key, ref)==null) {
    //we have locked on the key before inserting the element
    doSomethingElse();
   }
} finally {ref.lock.unlock();}

後で

Object value;
while (true) {
   LockedReference<T> ref = map.get(key)
   if (ref!=null) {
      ref.lock.lock(); 
      //there is no contention, unless a thread is already working on this entry
      try {
         if (map.containsKey(key)) {
          value=ref.value;
          break;      
         } else {
          /*key was removed between get and lock*/
         }
      } finally {ref.lock.unlock();} 
   } else value=null;
}  

より洗練されたアプローチは、書き換えであり、 (要素が配置された場合に実行される)を受け入れるConcurrentHashMapバージョンを持っています。しかし、それははるかに複雑です。putIfAbsentRunnable

基本的に、ConcurrentHashMapエントリごとに 1 つのロックとマップ全体の 1 つのグローバル ロックの中間にある、ロックされたセグメントを実装します。

于 2013-03-19T13:52:27.220 に答える
2

設計上、マップへのアクセスと他の操作をグループ化して、他のユーザーがマップにアクセスしないようにする必要がある場合は、それらをロックするしかありません。おそらく、この必要性を回避するために設計を再検討することができますか?

これは、マップへの他のすべてのアクセスを同じロックの背後でシリアル化する必要があることも意味します。

于 2013-03-19T13:41:30.477 に答える