2016-12-23 9 views
2

私はこのようなハッシュマップを作成する必要があります。出力マップのここJAVAの同時実行性とロックが

サンプル:

--------------------- 
| xxx | a b c d | 
| yyy | a' b' c' d' | 
| zzz | A B C D | 
--------------------- 

両方のスレッドがエントリの作成に貢献。したがって、例えば、スレッド1が記憶するように設定され、次のデータがあります

----------------------------------- 
| 1 | (xxx,a) (yyy,a') (zzz,A) | 
| 2 | (xxx,b) (yyy,b') (zzz,B) | 
----------------------------------- 

を、スレッド2が有する:


| 1 | (xxx,c) (yyy,c') (zzz,C) | 
| 2 | (xxx,d) (yyy,d') (zzz,D) | 
----------------------------------- 

をしたがって、同じ瞬間に、2つのスレッドが計算することができます。同じキー:スレッド1(xxx、a)とスレッド2(xxx、c)。

私が使用できるロック/アンロック機構は何ですか?

I)}は、以下の1

ReadWriteLock lock = new ReentrantReadWriteLock(); 

executor.submit(() -> { 
    lock.writeLock().lock(); 
    try { 
     sleep(1); 
     map.put("foo", "bar"); 
    } finally { 
    lock.writeLock().unlock(); 
    } 

を試みました。

しかし、私はハッシュマップ全体をロックしたくありません。私はロックを作成したいと思っていますが、処理中に両方のスレッドが同じキーで動作する場合にのみロックを作成します。

ありがとうございます。

答えて

2

まず、あなたが同時地図を使用する必要があります。

Map<String, List<Integer>> map = new ConcurrentHashMap<>(); 

第二に、あなたは、このように、複数のスレッドによってアクセス前に同期リストを移入する必要があります

for (String key : keys) { 
    map.put(key, Collections.synchronizedList(new ArrayList<>())); 
} 

このようにしてください:

List<Integer> list = map.get(key); 
if (list == null) { 
    map.putIfAbsent(key, Collections.synchronizedList(new ArrayList<>())); 
    list = map.get(key); 
} 

これだけです。今はすべてがスレッドセーフでなければなりません。

注:マップからフェッチする前にリストで作業することは重要です。並行性のために、複数のスレッドが不足しているリストをマップに追加しようとする可能性がありますが、そのうちの1つだけが成功します。したがって、不足しているリストを追加しようとした後は、常にマップから後で取り出します。

+0

キーがあらかじめわかっていないとどうなりますか? – BretC

+0

'map.putIfAbsent()'も使用して、初期化段階をスキップすることができます。しかし、値にアクセスする前に毎回これを行う必要があります。しかし、取得しようとすると最適化することができ、失敗した場合にのみ 'putIfAbsent()'を実行します。 – Harmlezz

+0

先ほど説明した代替のサンプルコードを追加しました。 – Harmlezz

0

ハッシュマップの代わりにハッシュテーブルを使用してください。ハッシュテーブルはスレッドセーフです(既に同期されています)。ロック/アンロックメカニズムを再度実装する必要はありません。

+0

したがって、同じ(共有)データ構造を両方のスレッドに渡して、それぞれが独自のデータ部分を格納できるかどうかを確認してください。 – Fab

1

地図から読み取って(キーがすでに存在するかどうかを確認するために)アトミック操作で更新する必要があるため、他の回答が必要な場合は確信できません。これが最もエレガントなソリューションですが、あなたはこのようなものを使用することができれば...

わからない

輸入はjava.util.HashSet。 import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap;

今すぐあなたのスレッドで、あなたが行うことができます
public class KeyLockMap<K, V> { 

    private Map<K, V> map; 

    private Set<K> lockKeys; 

    public KeyLockMap() { 
     map = new ConcurrentHashMap<K, V>(); 
     lockKeys = new HashSet<K>(); 
    } 

    public void lock(K key) throws InterruptedException { 
     synchronized(lockKeys) { 
      // Keep waiting while the lockKeys set contains the key we want to update 
      while(lockKeys.contains(key)) { 
       lockKeys.wait(); 
      } 
     } 
    } 

    public void unlock(K key) { 
     synchronized(lockKeys) { 
      lockKeys.remove(key); 
      // Notify any threads that may be waiting for this key to be removed 
      lockKeys.notifyAll(); 
     } 
    } 

    public Map<K, V> getMap() { 
     return map; 
    } 
} 

...

keyLockMap.lock(KEY); 
Map<String, List<Integer>> map = keyLockMap.getMap(); 
...do updates... 
keyLockMap.unlock(KEY); 

・ホープ、このことができます。


仕事ができる別の方法は、別のスレッドがputIfAbsentまずない場合、あなたが作成されたCopyOnWriteArrayListを取得します

List<Integer> newValue = new CopyOnWriteArrayList<>(); 
List<Integer> value = concurrentMap.putIfAbsent(KEY, newValue); 

if(value == null) { 
    value = newValue; 
} 

value.add(DATA); 

...例えば、ConcurrentHashMapの上で「putIfAbsent」を使用することですそのスレッドによってCopyOnWriteArrayListがスレッドセーフなので、データを追加することができます。

(最初の入力時にnullが返されるように編集されています...)

+0

java 8を使用している場合は、 'ConcurrentHashMap'と' putIfAbsent'を使用して、CopyOnWriteリストを値として使用することができます。 – BretC

+0

CopyOnWriteの手順をより良く説明できますか? – Fab

+0

ConcurrentHashMapがJava 7で導入されました – ddarellis

1

java.util.concurrentパッケージには既にMapインターフェイスののスレッドセーフ実装が含まれています。

しかし、リストも同期させる必要があります(下記のBretCのコメントを参照)。だから、おそらく最も便利な解決策はGuava synchronized multimapを使うことでしょう。そのような:

public static void main(String[] args) { 

    Multimap <String, Integer> map = Multimaps.synchronizedMultimap(HashMultimap.<String, Integer> create()); 

    // In Thread 1 
    map.put("foo", 1); 

    //In Thread 2 
    map.put("foo", 2); 

    System.out.println(map); // prints {foo=[1, 2]} 
} 
+0

スレッド1が地図を読み込んでキーが存在する(存在しない)場合はどうなり、スレッド2が地図を読み込んで同じキーが存在する(それはまだありません)... 1つのスレッドがもう一方のスレッドを上書きしないでしょうか? – BretC

+0

あなたは正しいです。私の答えを編集しました。 –

関連する問題