サーチ…


前書き

並行収集は、 同時に複数のスレッドによるアクセスを許可する[コレクション] [1]です。異なるスレッドは、通常、コレクションの内容を繰り返し処理し、要素を追加または削除します。コレクションは、コレクションが破損しないようにする責任があります。 [1]:http://stackoverflow.com/documentation/java/90/collections#t=201612221936497298484

スレッドセーフなコレクション

デフォルトでは、さまざまなCollection型はスレッドセーフではありません。

しかし、コレクションをスレッドセーフにすることはかなり簡単です。

List<String> threadSafeList = Collections.synchronizedList(new ArrayList<String>());
Set<String> threadSafeSet = Collections.synchronizedSet(new HashSet<String>());
Map<String, String> threadSafeMap = Collections.synchronizedMap(new HashMap<String, String>());

スレッドセーフなコレクションを作成するときは、スレッドセーフなラッパーを介してのみ、元のコレクションからアクセスしないでください。

Java SE 5

Java 5以降、 java.util.collectionsは、さまざまなCollections.synchronizedメソッドが不要な新しいスレッドセーフなコレクションがいくつかあります。

List<String> threadSafeList = new CopyOnWriteArrayList<String>();
Set<String> threadSafeSet = new ConcurrentHashSet<String>();
Map<String, String> threadSafeMap = new ConcurrentHashMap<String, String>();

コンカレントコレクション

並行コレクションは、スレッドセーフなコレクションの一般化であり、並行環境での幅広い使用を可能にします。

スレッドセーフなコレクションは複数のスレッドから安全に要素を追加または削除できますが、必ずしも同じコンテキストで安全な反復処理を行う必要はありません(1つのスレッドでコレクションを安全に反復できない場合があります。要素を削除する)。

これは、同時収集が使用される場所です。

繰り返しはaddAllremoveAll 、またはコレクションのコピー(コンストラクタやその他の手段による)、並べ替えなど、コレクション内のいくつかのバルクメソッドの基本実装であることが多いため、並行コレクションのユースケースは実際かなり大きいです。

たとえば、Java SE 5 java.util.concurrent.CopyOnWriteArrayListスレッドセーフと同時であるLisトンの実装、そのJavadocは述べています:

スナップショットスタイルのイテレータメソッドは、イテレータが作成された時点で配列の状態への参照を使用します。この配列は、イテレータの存続期間中は変更されないため、干渉は不可能であり、イテレータはConcurrentModificationExceptionをスローしないことが保証されています。

したがって、次のコードは安全です。

public class ThreadSafeAndConcurrent {

public static final List<Integer> LIST = new CopyOnWriteArrayList<>();

public static void main(String[] args) throws InterruptedException {
    Thread modifier = new Thread(new ModifierRunnable());
    Thread iterator = new Thread(new IteratorRunnable());
    modifier.start();
    iterator.start();
    modifier.join();
    iterator.join();
}

public static final class ModifierRunnable implements Runnable {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 50000; i++) {
                LIST.add(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static final class IteratorRunnable implements Runnable {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10000; i++) {
                long total = 0;
                for(Integer inList : LIST) {
                    total += inList;
                }
                System.out.println(total);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
}

反復に関するもう1つの同時収集はConcurrentLinkedQueueです。

イテレータは弱く一貫しており、イテレータの作成時または作成時のある時点でキューの状態を反映する要素を返します。彼らはjava.util.ConcurrentModificationExceptionを投げず、他の操作と並行して進めることができます。イテレータの作成以降にキューに含まれる要素は、一度だけ返されます。

コレクションが並行しているかどうかを確認するには、javadocをチェックする必要があります。 iterator()メソッド( "fail fast"、 "weakly consistent"などiterator()によって返されるイテレータの属性は、最も重要な属性です。

スレッドセーフであるが非並行の例

上記のコードでは、 LIST宣言を

public static final List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());

最新のマルチCPU /コアアーキテクチャでは統計的に例外が発生する可能性があります。

Collectionsユーティリティメソッドからの同期化されたCollectionsは、要素の追加/削除ではスレッドセーフですが、繰り返しはできません(それに渡される基底のコレクションは既に存在している場合を除きます)。

ConcurrentHashMapへの挿入

public class InsertIntoConcurrentHashMap
{

    public static void main(String[] args)
    {
        ConcurrentHashMap<Integer, SomeObject> concurrentHashMap = new ConcurrentHashMap<>();

        SomeObject value = new SomeObject();
        Integer key = 1;

        SomeObject previousValue = concurrentHashMap.putIfAbsent(1, value);
        if (previousValue != null)
        {
            //Then some other value was mapped to key = 1. 'value' that was passed to
            //putIfAbsent method is NOT inserted, hence, any other thread which calls
            //concurrentHashMap.get(1) would NOT receive a reference to the 'value'  
            //that your thread attempted to insert. Decide how you wish to handle             
            //this situation.
        }

       else
       {
            //'value' reference is mapped to key = 1.
       }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow