この記事では、HashMap をスレッドセーフに使用するための 4 つのテクニックについて説明します。
内部に 1 つのメソッド: スレッドごとに個別の HashMap を使用
以下の図に示すように、Tomcat はリクエストを受信すると、コントローラー、サービス層 Service、データベース アクセス層の関連メソッドを順番に呼び出します。
サービス層メソッドの serviceMethod にアクセスするたびに、メソッド本体内に別の HashMap が作成され、関連するリクエスト パラメータが HashMap にコピーされ、DAO メソッドが呼び出されてデータベース操作が実行されます。
各 HTTP 処理スレッドは、サービス層メソッド本体内に独自のインスタンスを持ちます。マルチスレッド環境では、同期操作を実行するHashMap
必要はありません。HashMap
これは、私たちが使用する最も一般的で最も安全な方法でもあり、CRUD の最も基本的な操作です。
2 構成データ: 初期書き込み、その後の読み取りのみ
システムの起動後、構成データをローカル キャッシュ HashMap にロードできます。構成情報が初期化された後は、それを書き込む必要はなく、読み取り操作のみが提供されます。
上の図は、HashMap オブジェクト configMap を内部に持つ非常に単純な構成クラス SimpleConfig を示しています。コンストラクターは初期化メソッドを呼び出します。初期化メソッドの内部ロジックは、構成データを HashMap に保存することです。
SimpleConfig クラスは getConfig メソッドを外部に公開します。メイン スレッドが SimpleConfig オブジェクトを初期化した後、他のスレッドが getConfig メソッドを呼び出すときは、読み取り操作のみが行われ、書き込み操作は行われないため、スレッドセーフになります。
3 読み取り/書き込みロック: 書き込み時のブロック、並列読み取り、読み取りの増加と書き込みの減少のシナリオ
読み取り/書き込みロックは、読み取りロックと書き込みロックの 2 つの部分に分かれたロックです。読み取りロックでは複数のスレッドが同時にロックを取得できますが、書き込みロックは相互排他ロックです。
そのルールは次のとおりです。<strong style="font-size: apply;line-height: 継承;color: rgb(255, 104, 39);">読み取りと読み取りは相互に排他的ではなく、読み取りと書き込みは相互に排他的です。書くことと書くことは相互に排他的</strong>であり、読むことが多く、書くことが少ないビジネス シナリオに適しています。
通常、ReadWriteLock を実装する ReentrantReadWriteLock を使用します。 ReadWriteLock インターフェイスも非常にシンプルで、主に内部的に 2 つのメソッドを提供し、それぞれ読み取りロックと書き込みロックを返します。
public interface ReadWriteLock {
//获取读锁
Lock readLock();
//获取写锁
Lock writeLock();
}
読み取り/書き込みロックの使用方法は次のとおりです。
- ReentrantReadWriteLock オブジェクトを作成します。ReadWriteLock を使用する場合、このオブジェクトは直接使用されませんが、内部の読み取りロックと書き込みロックを取得し、それぞれ lock/unlock メソッドを呼び出します。
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
- 共有データを読み取ります。
Lock readLock = readWriteLock.readLock();
readLock.lock();
try {
// TODO 查询共享数据
} finally {
readLock.unlock();
}
- 共有データを書き込みます。
Lock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
// TODO 修改共享数据
} finally {
writeLock.unlock();
}
次のコードは、ReadWriteLock を使用して HashMap をスレッドセーフに使用する方法を示しています。
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockCache {
// 创建一个 HashMap 来存储缓存的数据
private Map<String, String> map = new HashMap<>();
// 创建读写锁对象
private ReadWriteLock rw = new ReentrantReadWriteLock();
// 放对象方法:向缓存中添加一个键值对
public void put(String key, String value) {
// 获取写锁,以确保当前操作是独占的
rw.writeLock().lock();
try {
// 执行写操作,将键值对放入 map
map.put(key, value);
} finally {
// 释放写锁
rw.writeLock().unlock();
}
}
// 取对象方法:从缓存中获取一个值
public String get(String key) {
// 获取读锁,允许并发读操作
rw.readLock().lock();
try {
// 执行读操作,从 map 中获取值
return map.get(key);
} finally {
// 释放读锁
rw.readLock().unlock();
}
}
}
読み取り/書き込みロックを使用して HashMap を操作するのは、非常に古典的な手法です。メッセージ ミドルウェア RockeMQ NameServer (ネーム サービス) は、この手法を通じてルーティング情報を保存し、クエリします。
さらに、読み取り/書き込みロックは複数の HashMap を操作できます。ConcurrentHashMap と比較して、ReadWriteLock はキャッシュ オブジェクトの粒度を制御でき、柔軟性が高くなります。
4 Collections.synchronizedMap: 読み取りと書き込みの両方がロックされています
次のコードは、複数のスレッドで userMap を使用する場合、
static Map<Long, User> userMap = Collections.synchronizedMap(new HashMap<Long, User>());
synchronizedMap メソッドを入力します。
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
SynchronizedMap には内部にオブジェクト ロック オブジェクト ミューテックスが含まれており、これは本質的に HashMap の読み取りおよび書き込み操作を再実装するラッパー クラスであり、読み取りまたは書き込みのたびに synchronized キーワードがスレッドの安全性を確保するために使用されることがわかります。手術。
Collections.synchronizedMap 手法は使用するのが非常に簡単ですが、読み書きされるたびにロックされ、パフォーマンスがあまり良くないことを理解する必要があります。
5 まとめ
この記事では、著者は HashMap を使用するための 4 つのスレッドセーフなテクニックをまとめています。
1. メソッド内: 各スレッドは個別の HashMap を使用します
これは最も一般的に使用され、非常に信頼性の高い方法です。各スレッドはメソッド body 内にインスタンスを作成しますHashMap
。マルチスレッド環境では、HashMap
同期操作は必要ありません。
2. 構成データ: 初回書き込み、以降読み取りのみ
ミドルウェアが開始されると、構成ファイルが読み取られ、構成データが HashMap に書き込まれます。メイン スレッドが書き込みを完了すると、それ以降書き込み操作は行われず、スレッド セーフを引き起こすことなく、他のスレッドがそのミドルウェアを読み取ることができます。問題。
3. 読み取り/書き込みロック: 書き込み時のブロック、並列読み取り、読み取りの増加と書き込みの減少のシナリオ
読み取り/書き込みロックは、読み取りロックと書き込みロックの 2 つの部分に分かれたロックです。読み取りロックでは複数のスレッドが同時にロックを取得できますが、書き込みロックは相互排他ロックです。
そのルールは次のとおりです。<strong style="font-size: apply;line-height: 継承;color: rgb(255, 104, 39);">読み取りと読み取りは相互に排他的ではなく、読み取りと書き込みは相互に排他的です。書くことと書くことは相互に排他的</strong>であり、読むことが多く、書くことが少ないビジネス シナリオに適しています。
読み取り/書き込みロックを使用して HashMap を操作するのは、非常に古典的な手法です。メッセージ ミドルウェア RockeMQ NameServer (ネーム サービス) は、この手法を通じてルーティング情報を保存し、クエリします。
4. Collections.synchronizedMap: 読み取りと書き込みの両方がロックされています
Collections.synchronizedMap メソッドは、デコレータ パターンを使用して、スレッド非安全な HashMap にスレッド安全なデコレータ クラス SynchronizedMap を提供します。
SynchronizedMap は、HashMap の操作がスレッド セーフであることを間接的に保証するために使用され、SynchronizedMap の基礎となる層も synchronized キーワードを使用して操作のスレッド セーフを保証します。
「Celebrated More Than Years 2」の海賊版リソースが npm にアップロードされたため、npmmirror は unpkg サービスを停止せざるを得なくなり、 最初の創設者の 数百人が参加して、一斉に米国に向かいました。 フロントエンド視覚化ライブラリと Baidu の有名なオープンソース プロジェクト ECharts - Fish 詐欺師をサポートするために「海へ行く」が、TeamViewer を使用して 398 万を送金しました。リモート デスクトップ ベンダーは何をすべきでしょうか? 周宏宜: Google に残された時間はあまり多くありません。すべての製品をオープンソースにすることが推奨されています。 ある有名なオープンソース企業の元従業員が、部下から異議を申し立てられた後、激怒しました。妊娠中の女性従業員を解雇しました。Google は Android 仮想マシンで ChromeOS を実行する方法を示しました。 ここで time.sleep(6) はどのような役割を果たしますか? マイクロソフト、中国のAIチームが「米国のために荷造りしている」という噂に反応 人民日報オンラインはオフィスソフトのマトリョーシカのような課金についてコメント:「セット」を積極的に解決することによってのみ、私たちは未来を手に入れることができる