2009-09-08 14 views
38

私はLinkedHashMapであり、スレッドセーフなデータ構造が必要です。javaには "LinkedConcurrentHashMap"データ構造がありますか?

どうすればいいですか?

+6

質問が不十分です。なぜあなたは "LinkedHashMap"である必要があると言いますか?あなたは本当に何の行動をしていますか? –

+0

類似の質問:http://stackoverflow.com/questions/1815646/how-to-implement-concurrenthashmap-with-features-similar-in-linkedhashmap – Vadzim

答えて

4

Collections.synchronizedMap(new LinkedHashMap())

+2

しかし、コンポジットを実行したい場合は、手動で同期する必要がありますConcurrentHashMap –

24

あなたは挿入順序を保持し、同期ハッシュマップを取得するにはCollections.synchronizedMapで地図をラップすることができます。これはConcurrentHashMapほど効率的ではなく(ConcurrentMapの余分なインターフェースメソッドを実装していませんが)(いくらか)スレッドセーフな動作を行います。

Googleの強力なコレクションでさえ、まだこの特定の問題を解決していないようです。しかし、問題に取り組もうとしているのはone projectです。

並列処理の例外が発生する可能性があるという意味では、反復処理はまだスレッドセーフではないため、同期についてはいくぶん言います。

+1

+1によって提供される余分なもののような操作。私はちょうど私がそれを覚えているようにそれを追加したいのですが、理由はありません。なぜならアクセス順序で動作を保持するためには、とにかくテーブル全体をロックすることになるでしょう通常のLinkedHashMapの周りの同期を行うことは、ほとんどの場合ほぼ同等に効率的になります。説明は白い灰色かもしれませんが、それは私には意味があります。 – Fredrik

+1

(免責事項:私は投票もダウンダウンもしなかった)このアプローチが嫌いです。同期!=同時。マップ上で集中的な並列ジョブがある場合、n-1スレッドは常にブロックされます。コンカレントはほとんどの状況でブロックする必要があります。 – juanmf

3

ConcurrentHashMapはMapインターフェイスにないいくつかの重要な余分なメソッドを提供しているので、LinkedHashMapをsynchronizedMapでラップするだけで同じ機能が提供されるわけではなく、putIfAbsent ConcurrentHashMapを非常に便利にするメソッド(key、oldValue、newValue)とremove(key、oldValue)を置き換えます。

あなたが望むものを実装しているApacheライブラリがない場合は、おそらくLinkedHashMapを使用し、自分の適切なsynchronized {}ブロックを用意する必要があります。

+0

6年後にダウンして、何の理由もありません。私の答えが間違った質問に答えるように質問が変わったからではありません。アイデア? –

0

答えはほとんどありません。ソートされたConcurrentHashMap(LinkedHashMapのようなもの)に相当するものは何もありません。他の人が指摘しているように、Collections.synchronizedMap(-yourmap-)を使用してコレクションをラップすることはできますが、同じレベルのきめの細かいロックは得られません。すべての操作でマップ全体がブロックされます。

マップへのアクセスの周りでsynchronizedを使用すること(そのような場合はもちろん、ダーティリードは気にしないかもしれません)、マップの周りにラッパーを記述して、ロックしないでください。

+1

ハッシュマップへの非同期読み込みがある場合は、特にマルチCPU環境で読み込みがダーティであるだけでなく、破損したり、例外をスローしたりする可能性があります。 – Yishai

+2

同時更新による干渉のため、HashMapが無限ループになるケースがありました。 –

10

この問題にはさまざまなアプローチがあります。

Collections.synchronizedMap(new LinkedHashMap()); 

他の回答も示唆していますが、これには注意が必要ないくつかの問題があります。特に、コレクションを反復処理するときにコレクションの同期ロックを保持する必要があり、その反復処理が完了するまで他のスレッドがコレクションにアクセスできないことがよくあります。 (See Java theory and practice: Concurrent collections classes)。たとえば:

new ConcurrentHashMap(); 

を使用して

synchronized(map) { 
    for (Object obj: map) { 
     // Do work here 
    } 
} 

は、おそらくあなたはそれを反復処理するコレクションをロックする必要はありませんよう、より良い選択です。

最後に、より多くの機能プログラミングアプローチを検討したいと思うかもしれません。つまり、マップは本質的に不変であると考えることができます。既存のマップに追加する代わりに、古いマップの内容と新しい追加内容を含む新しいマップを作成します。これは最初はかなり奇妙に聞こえるが、実際は道だScala deals with concurrency and collections

+0

Scalaがどのように問題に取り組んでいるかを指摘してくれてありがとう。それはかなり目が離せない。 –

+0

私は、JavaがCopyOnWriteArrayListでそのアプローチを使用していると思います。すべての書き込み後にリストをコピーし、パフォーマンスが広がりますが、それは私が推測する方法です。 –

7

Googleコードで利用できるone implementationがある。サイトの見積もり:

ソフトウェアキャッシュとして使用するための高性能バージョンのjava.util.LinkedHashMap。

デザイン

  • 同時リンクリストは、立ち退きの順序を提供するためのConcurrentHashMapを介して実行されます。
  • 挿入およびアクセスの順序付き排除ポリシー(FIFO、LRU、およびSecond Chance)をサポートします。
+2

このプロジェクトの主なコミッターはGoogleのソフトウェアエンジニア、Ben Manesです。そのため、かなり信頼できます(IMHO)。 – Marco

4

は、Java SE/EE 6以降でのみ使用可能ConcurrentSkipListMapのを、使用することができます。キーは、自然順序付けに従ってソートされているので、順序は整っています。あなたはComparatorを持っているか、Comparableオブジェクトのキーを作る必要があります。リンクされたハッシュマップの動作を模倣するために(反復順序はエントリが追加された順序です)、キーオブジェクトを実装して、指定された他のオブジェクトよりも大きくなるように比較します。 )。 http://www.ibm.com/developerworks/java/library/j-jtp07233.htmlに記載されているように、ラップされた同期化されたリンク付きハッシュマップでは不十分でした: "synchronizedコレクションラッパー、synchronizedMapおよびsynchronizedListは条件付きスレッドセーフとも呼ばれます。リスト1の最初のスニペットは、共通のput-if-absentイディオムを示しています。エントリがマップにまだ存在しない場合は、追加します。 containsKey()メソッドが返す時刻とput()メソッドが呼び出された時刻との間に、同じキーを持つ値を別のスレッドが挿入する可能性があります。正確に一度の挿入を確実にするには、マップmで同期する同期ブロックを持つステートメントのペア。

したがって、通常のConcurrentHashMapより3〜5倍遅いConcurrentSkipListMapが役立ちます。

+2

残念ながら、挿入順序を定義するカスタムコンパレータは機能しません。これは、compare()に渡された最初のオブジェクトが新しく挿入されたオブジェクトであることを前提としています。しかし、ConcurrentSkipListマップはiterating(少なくとも 'map.entrySet()')するとcompareを呼び出します。この場合、2つのオブジェクトのどちらが最初に挿入されたものかわかりません。このオブジェクトに作成日が含まれていない限り、これはキーのような文字列の一般的なケースでは失敗します。 –

1

私は、挿入順に基づいて同期境界付きLRUマップを試しました。LinkedConcurrentHashMap; 同期化のための読み取り/書き込みロックを使用します。 したがって、イテレータを使用しているとき。 ConcurrentModificationExceptionを避けるためにWriteLockを取得する必要があります。
これはより良いですCollections.synchronizedMap.

public class LinkedConcurrentHashMap<K, V> { 

    private LinkedHashMap<K, V> linkedHashMap = null; 
    private final int cacheSize; 
    private ReadWriteLock readWriteLock = null; 

    public LinkedConcurrentHashMap(LinkedHashMap<K, V> psCacheMap, int size) { 
     this.linkedHashMap = psCacheMap; 
     cacheSize = size; 
     readWriteLock=new ReentrantReadWriteLock(); 
    } 

    public void put(K key, V value) throws SQLException{ 
     Lock writeLock=readWriteLock.writeLock(); 
     try{ 
      writeLock.lock(); 
      if(linkedHashMap.size() >= cacheSize && cacheSize > 0){ 
       K oldAgedKey = linkedHashMap.keySet().iterator().next(); 
       remove(oldAgedKey); 
      } 
      linkedHashMap.put(key, value); 
     }finally{ 
      writeLock.unlock(); 
     } 
    } 

    public V get(K key){ 
     Lock readLock=readWriteLock.readLock(); 
     try{ 
      readLock.lock(); 
      return linkedHashMap.get(key); 
     }finally{ 
      readLock.unlock(); 
     } 
    } 

    public boolean containsKey(K key){ 
     Lock readLock=readWriteLock.readLock(); 
     try{ 
      readLock.lock(); 
      return linkedHashMap.containsKey(key); 
     }finally{ 
      readLock.unlock(); 
     } 
    } 

    public V remove(K key){ 
     Lock writeLock=readWriteLock.writeLock(); 
     try{ 
      writeLock.lock(); 
      return linkedHashMap.remove(key); 
     }finally{ 
      writeLock.unlock(); 
     } 
    } 

    public ReadWriteLock getLock(){ 
     return readWriteLock; 
    } 

    public Set<Map.Entry<K, V>> entrySet(){ 
     return linkedHashMap.entrySet(); 
    } 
} 
+0

'ConcurrentMap 'を実装すると、無料で同期が取れます( 'ReadWriteLock'を必要としません)。 – naXa

+0

@naXa LRUマップを試したところ、地図がいっぱいですが、新しい要素を追加すると、最近使用された要素のリースも削除されるはずです。したがって、ロック実装が必要です。 –

+0

そして、私はLRUMapがtrueになるためには、accessOrderブール値を使ってLinkedHashMap(int initialCapacity、float loadFactor、boolean accessOrder)を使って実装できることを知っています。しかし、同期されていません。 –

関連する問題