同期Javaコレクションの紹介
1. 概要
collections frameworkはJavaの重要なコンポーネントです。 膨大な数のインターフェイスと実装を提供するため、さまざまなタイプのコレクションを簡単に作成および操作できます。
単純な非同期コレクションを使用することは全体的に簡単ですが、マルチスレッド環境(別名: 並行プログラミング)。
したがって、Javaプラットフォームは、Collectionsクラス内に実装されたさまざまな同期wrappersを通じて、このシナリオを強力にサポートします。
これらのラッパーにより、いくつかの静的ファクトリメソッドを使用して、提供されたコレクションの同期ビューを簡単に作成できます。
このチュートリアルでは、we’ll take a deep dive into these *static synchronization wrappers. Also, we’ll highlight the difference between synchronized collections and concurrent collections。*
2. synchronizedCollection()メソッド
このまとめで取り上げる最初の同期ラッパーは、synchronizedCollection()メソッドです。 名前が示すように、it returns a thread-safe collection backed up by the specified Collection。
ここで、この方法の使用方法をより明確に理解するために、基本的な単体テストを作成しましょう。
Collection syncCollection = Collections.synchronizedCollection(new ArrayList<>());
Runnable listOperations = () -> {
syncCollection.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));
};
Thread thread1 = new Thread(listOperations);
Thread thread2 = new Thread(listOperations);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
assertThat(syncCollection.size()).isEqualTo(12);
}
上記のように、このメソッドを使用して、提供されたコレクションの同期ビューを作成するのは非常に簡単です。
メソッドが実際にスレッドセーフなコレクションを返すことを示すために、最初にいくつかのスレッドを作成します。
その後、ラムダ式の形式で、Runnableインスタンスをコンストラクターに挿入します。 Runnableは関数型インターフェースであるため、ラムダ式に置き換えることができることに注意してください。
最後に、各スレッドが同期コレクションに効果的に6つの要素を追加することを確認するだけなので、最終的なサイズは12です。
3. synchronizedList()メソッド
同様に、synchronizedCollection()メソッドと同様に、synchronizedList()ラッパーを使用して同期されたListを作成できます。
予想通り、the method returns a thread-safe view of the specified List:
List syncList = Collections.synchronizedList(new ArrayList<>());
当然のことながら、synchronizedList()メソッドの使用は、上位レベルの対応するsynchronizedCollection()とほぼ同じように見えます。
したがって、前の単体テストで行ったように、同期されたListを作成すると、複数のスレッドを生成できます。 その後、それらを使用して、スレッドセーフな方法でターゲットListにアクセス/操作します。
さらに、同期されたコレクションを繰り返し処理し、予期しない結果を防ぎたい場合は、ループの独自のスレッドセーフ実装を明示的に提供する必要があります。 したがって、synchronizedブロックを使用してこれを実現できます。
List syncCollection = Collections.synchronizedList(Arrays.asList("a", "b", "c"));
List uppercasedCollection = new ArrayList<>();
Runnable listOperations = () -> {
synchronized (syncCollection) {
syncCollection.forEach((e) -> {
uppercasedCollection.add(e.toUpperCase());
});
}
};
同期されたコレクションを反復処理する必要があるすべての場合、このイディオムを実装する必要があります。 これは、同期されたコレクションの反復がコレクションへの複数の呼び出しを通じて実行されるためです。 したがって、単一のアトミック操作として実行する必要があります。
The use of the synchronized block ensures the atomicity of the operation。
4. synchronizedMap()メソッド
Collectionsクラスは、synchronizedMap().と呼ばれる別のきちんとした同期ラッパーを実装します。これを使用して、同期されたMapを簡単に作成できます。
The method returns a thread-safe view of the supplied Map implementation:
Map syncMap = Collections.synchronizedMap(new HashMap<>());
5. synchronizedSortedMap()メソッド
synchronizedMap()メソッドの対応する実装もあります。 これはsynchronizedSortedMap()と呼ばれ、同期されたSortedMapインスタンスを作成するために使用できます。
Map syncSortedMap = Collections.synchronizedSortedMap(new TreeMap<>());
6. synchronizedSet()メソッド
次に、このレビューに進むと、synchronizedSet()メソッドがあります。 その名前が示すように、最小限の手間で同期されたSetsを作成できます。
The wrapper returns a thread-safe collection backed by the specified Set:
Set syncSet = Collections.synchronizedSet(new HashSet<>());
7. synchronizedSortedSet()メソッド
最後に、ここで紹介する最後の同期ラッパーはsynchronizedSortedSet()です。
これまでに確認した他のラッパー実装と同様に、the method returns a thread-safe version of the given SortedSet:
SortedSet syncSortedSet = Collections.synchronizedSortedSet(new TreeSet<>());
8. 同期コレクションと同時コレクション
ここまで、コレクションフレームワークの同期ラッパーについて詳しく見てきました。
それでは、ConcurrentHashMapやBlockingQueueの実装などの the differences between synchronized collections and concurrent collectionsに焦点を当てましょう。
8.1. 同期されたコレクション
Synchronized collections achieve thread-safety through intrinsihttps://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html[c locking], and the entire collections are locked。 組み込みロックは、ラップされたコレクションのメソッド内の同期ブロックを介して実装されます。
予想されるように、同期されたコレクションは、マルチスレッド環境でのデータの整合性/整合性を保証します。 ただし、一度に1つのスレッドのみがコレクションにアクセスできるため、パフォーマンスが低下する可能性があります(別名: 同期アクセス)。
synchronizedメソッドとブロックの使用方法の詳細なガイドについては、トピックのour articleを確認してください。
8.2. 並行コレクション
Concurrent collections (e.g. ConcurrentHashMap), achieve thread-safety by dividing their data into segments。 たとえば、ConcurrentHashMapでは、異なるスレッドが各セグメントでロックを取得できるため、複数のスレッドが同時にMapにアクセスできます(別名 同時アクセス)。
同時スレッドアクセスの固有の利点により、同時コレクションはmuch more performant than synchronized collectionsです。
そのため、使用するスレッドセーフコレクションの種類の選択は、各ユースケースの要件に依存し、それに応じて評価する必要があります。
9. 結論
In this article, we took an in-depth look at the set of synchronization wrappers implemented within the Collections class。
さらに、同期コレクションと同時コレクションの違いを強調し、スレッドセーフを実現するためにそれらが実装するアプローチにも注目しました。
いつものように、この記事に示されているすべてのコードサンプルはGitHubで入手できます。