2011-01-17 2 views
4

これは私がマップを持っており、安全にスレッドのキーセット公開したいかなり一般的な発生でなければなりません:今すぐスレッド・セーフな方法は、()

public MyClass { 
    Map<String,String> map = // ... 
    public final Set<String> keys() { 
    // returns key set 
    } 
} 

を、私の「マップ」場合は、スレッドではありません

public final Set<String> keys() { 
    return map.keySet(); 
    } 

ともされていない:-safe、これは安全ではないん

public final Set<String> keys() { 
    return Collections.unmodifiableSet(map.keySet()); 
    } 

だから私のような、コピーを作成する必要があります。

public final Set<String> keys() { 
    return new HashSet(map.keySet()); 
    } 

しかし、これは、そのコンストラクタがパラメータの要素を横断してadd()するため、安全ではないようです。したがって、このコピーが実行されている間に、ConcurrentModificationExceptionが発生することがあります。それでは

public final Set<String> keys() { 
    synchronized(map) { 
     return new HashSet(map.keySet()); 
    } 
    } 

は、ソリューションのように思えます。これは正しいか?

答えて

4

地図上で使用されているすべての場所で同期をとる予定がない限り、その解決策はあまり役に立ちません。それを同期させても、他の誰かが同時にその上でメソッドを呼び出すのを止めることはできません。それだけでも、それらが同期することができなくなります。

誰かが反復している間に同時パットと削除が必要な場合は、まずはConcurrentHashMapを使用してください。クラスが提供する並行性の振る舞いが必要なものでない場合は、完全に同期化されたマップを使用するだけで済みます。

+0

マップ自体は公開されておらず、そのキーセットのみが公開されています。そしてその鍵のコピーがそれにセットされます。 – Jake

+0

はい、私はそれが何か変わるとは思わない?そのブロックを同期させても、修正しようとしていたハザードは修正されません。 – Affe

+0

あなたが 'ConcurrentModificationException'を心配している場合は、マップが非スレッドセーフな方法で公開されていることを意味します。 –

2

良い質問です。私はGoogle Guavaライブラリを使用します。より具体的にはcom.google.common.collect.ImmutableSet.copyOf(Collection<? extends E>)方法。ドキュメントでは、このメソッドはスレッドセーフであると言われています。

+0

「このメソッドは、要素が現在別のスレッドによって変更されている同期コレクションまたは並行コレクションであっても安全に使用できます。私は私の "マップ"が同期していることを保証していない。 – Jake

+0

@ Jake、地図が同期していない場合、これは解決できない問題です。 – finnw

+0

@Jake。私はあなたが実行時にあなたのマップを変更するつもりかと思います。いいえの場合は、ImmutableMapを使用できます。そうでない場合は、ConcurrentHashMap(Affeによって掲載されたソリューション)を使用することが、本当にあなたにとって最良の選択です。 –

-1

Collections.UnmodifiableMapを使用して一時的なマップを作成し、次にキーセットを反復することができます。

+0

これは、基礎となるマップがスレッドセーフではない場合、ConcurrentModificationExceptionから保護しません。 – Jake

+0

変更不可能なコレクションがある場合は、基になるマップで操作を削除または追加することはできません。それではどうやってスレッドセーフではないと言いますか? –

+0

@SureshSankar UnmodifiableMapで使用した実際のコレクションは、他のスレッドによって変更される可能性があります。 –

0

また、ConcurrentHashMapを使用することもできます。そのkeySet()はスレッドセーフなので、同期やコピーの必要はありません。

0

スレッドセーフなイテレータと要素の正確なスナップショットが必要な場合は、繰り返し処理を行ってください。

public class ThreadSafeIteratorConcurrentMap 
{ 
    private ConcurrentMap<String, String> itrSafeMap = null; 

    public ThreadSafeIteratorConcurrentCollection() { 
      itrSafeMap = new ConcurrentHashMap<String, String> 
    } 

    public void synchronized put(psConference conference, String p_key) 
    { 
     itrSafeMap.putIfAbsent(p_key, conference); 
    } 

    public psConference getConference(String p_key) 
    { 
     return (itrSafeMap.get(p_key)); 
    } 

    public void synchronized remove(String p_key) 
    { 
     itrSafeMap.remove(p_key); 
    } 

    public boolean containsKey(String p_key) 
    { 
     return itrSafeMap.containsKey(p_key); 
    } 

    // Get the size of the itrSafeMap. 
    public int size() 
    { 
     return itrSafeMap.size(); 
    } 

    public Iterator<String> valueIterator() 
    { 
     return (itrSafeMap.values().iterator()); 
    } 

    public Iterator<String> keyIterator() 
    { 
     return (itrSafeMap.keySet().iterator()); 
    } 

} 

次に、要素の正確なスナップショットを持つスレッドセーフイテレータが必要な場所。下記のような同期ブロックで使用してください。

synchronized(threadSafeIteratorConcurrentMapObject) { 

     Iterator<String> keyItr = threadSafeIteratorConcurrentMapObject.keyIterator(); 
     while(keyItr.hasNext()){ 
     // Do whatever 
     } 
    } 

反復処理中にコレクションを変更しても構わない場合は、イテレータの作成時に要素のスナップショットに集中するだけです。同期ブロックがなければ、keyItrを使用できます。これはすでにスレッドセーフです。それはConcurrentModificationExceptionを経由しません。

関連する問題