同期Javaコレクションの紹介
1概要
コレクションフレームワーク
は、Javaの主要コンポーネントです。それは私達が簡単な方法で異なったタイプのコレクションを作成して操作することを可能にする多数のインターフェースと実装を提供します。
単純な非同期のコレクションを使用することは全体的に単純ですが、マルチスレッド環境(並行プログラミング)で作業する場合には、手間がかかりエラーが発生しやすくなります。
したがって、Javaプラットフォームは、
https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection(java.util)内に実装されたさまざまな同期
wrappers
を通じて、このシナリオを強力にサポートします。 Collection)[コレクション]
クラス。
これらのラッパーは、いくつかの静的なファクトリメソッドによって、供給されたコレクションの同期ビューを簡単に作成することを可能にします。
このチュートリアルでは、これらの静的同期ラッパーについて詳しく説明します。また、同期コレクションと並行コレクションの違いについても説明します** 。
2
synchronizedCollection()
メソッド
このまとめで取り上げる最初の同期ラッパーは
synchronizedCollection()
メソッドです。名前が示すように、
指定されたhttps://docs.oracle.com/javase/8/docs/api/java/util/Collection.html[
Collection
]
でバックアップされたスレッドセーフなコレクションを返します。
では、この方法の使い方をより明確に理解するために、基本的な単体テストを作成しましょう。
Collection<Integer> 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);
}
上に示したように、この方法で供給されたコレクションの同期ビューを作成することは非常に簡単です。
このメソッドが実際にスレッドセーフなコレクションを返すことを実証するために、まずスレッドをいくつか作成します。
その後、https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html[
Runnable
]インスタンスをラムダ式の形式でそれらのコンストラクタに挿入します。
Runnable
は機能的なインターフェースなので、ラムダ式に置き換えることができます。
最後に、各スレッドが同期コレクションに6つの要素を効果的に追加することを確認したので、最終的なサイズは12です。
3
synchronizedList()
メソッド
同様に、
synchronizedCollection()
メソッドと同様に、
synchronizedList()
ラッパーを使用して同期化されたhttps://docs.oracle.com/javase/8/docs/api/java/util/List.htmlを作成できます。 is-external = true[
List
]。
予想通り、このメソッドは指定された
List
のスレッドセーフなビューを返します。
List<Integer> syncList = Collections.synchronizedList(new ArrayList<>());
当然のことですが、
synchronizedList()
メソッドの使用は、その上位レベルのオブジェクトである
synchronizedCollection()
とほぼ同じです。
したがって、前回の単体テストで行ったように、同期化された
List
を作成したら、複数のスレッドを生成できます。それが終わったら、それらを使用してターゲット
List
にスレッドセーフでアクセス/操作します。
さらに、同期されたコレクションを繰り返し処理して予期しない結果を防ぎたい場合は、スレッドセーフなループの実装を明示的に提供する必要があります。したがって、
synchronized
ブロックを使用してそれを実現できます。
List<String> syncCollection = Collections.synchronizedList(Arrays.asList("a", "b", "c"));
List<String> uppercasedCollection = new ArrayList<>();
Runnable listOperations = () -> {
synchronized (syncCollection) {
syncCollection.forEach((e) -> {
uppercasedCollection.add(e.toUpperCase());
});
}
};
同期コレクションを反復処理する必要がある場合はすべて、このイディオムを実装する必要があります。これは、同期されたコレクションに対する繰り返しが、コレクションへの複数の呼び出しを通じて実行されるためです。したがって、それらは単一のアトミック操作として実行される必要があります。
-
synchronized
ブロックを使用すると、操作の原子性が保証されます。
4
synchronizedMap()
メソッド
Collections
クラスは、
synchronizedMap()という別の同期ラッパーを実装しています。
地図__]。
このメソッドは、提供された
Map
実装のスレッドセーフビューを返します。
Map<Integer, String> syncMap = Collections.synchronizedMap(new HashMap<>());
5
synchronizedSortedMap()
メソッド
synchronizedMap()
メソッドの対応する実装もあります。これは
synchronizedSortedMap()
と呼ばれ、同期
SortedMap
インスタンスを作成するために使用できます。
Map<Integer, String> syncSortedMap = Collections.synchronizedSortedMap(new TreeMap<>());
6.
synchronizedSet()
メソッド
次に、このレビューを続けると、
synchronizedSet()
メソッドがあります。
その名前が示すように、最小限の手間で同期された
Sets
を作成できます。
-
ラッパーは、指定された
Set
** を基にしたスレッドセーフなコレクションを返します。
Set<Integer> syncSet = Collections.synchronizedSet(new HashSet<>());
7.
synchronizedSortedSet()
メソッド
最後に、ここで紹介する最後の同期ラッパーは
synchronizedSortedSet()
です。
これまでに検討した他のラッパー実装と同様に、
このメソッドは、指定された
httpsのスレッドセーフバージョンを返します。[ソートセット]
:
SortedSet<Integer> syncSortedSet = Collections.synchronizedSortedSet(new TreeSet<>());
8同期コレクションと並行コレクション
ここまでは、コレクションフレームワークの同期ラッパーを詳しく見てきました。
それでは、https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html[
ConcurrentHashMap
]のように、
同期コレクションと並行コレクションの違い
に注目しましょう。および
BlockingQueue
の実装。
** 8.1. 同期コレクション
**
-
同期コレクションはhttps://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html[intrinsi]
c locking
をクリックすると、コレクション全体がロックされます** 。組み込みロックは、ラップされたコレクションのメソッド内の同期ブロックを介して実装されます。
ご想像のとおり、同期コレクションはマルチスレッド環境でデータの一貫性/完全性を保証します。ただし、一度に1つのスレッドしかコレクションにアクセスできないため、パフォーマンスが低下する可能性があります(別名同期アクセス)。
synchronized
メソッドとブロックの使用方法に関する詳細なガイドについては、このトピックに関するhttps://www.baeldung.com/java-synchronized[our article]を参照してください。
8.2. 並行コレクション
-
並行コレクション(
ConcurrentHashMapなど)は、それらのデータをセグメントに分割することによってスレッドセーフを実現します** 。たとえば、
ConcurrentHashMap
では、さまざまなスレッドが各セグメントのロックを取得できるため、複数のスレッドが同時に
Map__にアクセスできます(並行アクセス)。
並行スレッドアクセスには固有の利点があるため、並行コレクションは同期コレクションよりもはるかにパフォーマンスが優れています。
したがって、どのタイプのスレッドセーフなコレクションを使用するかの選択は、各ユースケースの要件によって異なり、それに応じて評価する必要があります。
9結論
-
この記事では、
Collections
クラス内に実装されている同期ラッパーのセットについて詳しく調べました** 。
さらに、同期コレクションと並行コレクションの違いを強調し、スレッドセーフを実現するためにそれらが実装するアプローチについても調べました。
いつものように、この記事で示したすべてのコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/core-java-collections/src/test/java/com/baeldung/synchronizedcollections/testから入手できます。[GitHub]