[解決済み] 複数のスレッドから java.util.HashMap の値を取得することは安全ですか(変更なし)?
質問
マップを構築し、一度初期化したら二度と変更しないケースがあります。 しかし、複数のスレッドから(get(key)のみで)アクセスされることになる。 このような場合
java.util.HashMap
をこのような形で使用することは可能でしょうか?
(現在、私は嬉々として
java.util.concurrent.ConcurrentHashMap
しかし、単純な
HashMap
で十分だと思います。 したがって、この質問は
ではない
どれを使えばいいのか、性能の問題でもありません。 むしろ問題は、"安全でしょうか?")です。
どのように解決するのですか?
あなたの慣用句は安全です
もし
への参照は
HashMap
は
無事公開
. の内部に関することよりも、むしろ
HashMap
そのものです。
安全な出版
は、構築スレッドが他のスレッドからマップへの参照を見えるようにする方法を扱います。
基本的に、ここで起こりうる唯一の競争は
HashMap
と、そのスレッドが完全に構築される前にアクセスする可能性のある読み取りスレッドがあります。ほとんどの議論は、マップオブジェクトの状態がどうなるかについてですが、これは決して変更されないので関係ありません。
HashMap
の参照が発行されます。
例えば、このような地図を公開することを想像してください。
class SomeClass {
public static HashMap<Object, Object> MAP;
public synchronized static setMap(HashMap<Object, Object> m) {
MAP = m;
}
}
... そして、ある時点で
setMap()
がマップ付きで呼び出され、他のスレッドが
SomeClass.MAP
にアクセスし、このようにNULLをチェックします。
HashMap<Object,Object> map = SomeClass.MAP;
if (map != null) {
.. use the map
} else {
.. some default behavior
}
これは
安全ではない
のように見えるかもしれないのに。問題は
前に起こる
のセットとの関係
SomeObject.MAP
と、その後に別のスレッドで読み込まれるため、読み込むスレッドは、部分的に構築されたマップを自由に見ることができます。これによって、かなり多くのことが可能になります
なんでも
のようなことができ、実際にも
読み込みスレッドを無限ループに陥れる
.
安全にマップを公開するためには
起こる前
という関係があります。
リファレンスの記述
を使用します。
HashMap
(という
出版物
)と、その参照先の読者(すなわち消費者)の2つからなる。便利なことに、覚えやすい方法はいくつかあるだけです。
達成する
その
[1]
:
- 適切にロックされたフィールドを介して、参照を交換する ( JLS 17.4.5 )
- 静的イニシャライザーを使って、ストアの初期化を行う ( JLS 12.4 )
- volatileフィールドを介して参照を交換する ( JLS 17.4.5 )、またはこのルールの結果として、AtomicXクラスを通じて
- 値を最終フィールドに初期化する ( JLS 17.5 ).
今回のシナリオで最も興味深いのは、(2)、(3)、(4)です。特に(3)は、私が上に書いたコードにそのまま適用されます。
MAP
に変更します。
public static volatile HashMap<Object, Object> MAP;
を見た読者は、すべてがうまくいくでしょう。
NULLでない
の値は、必ずしも
前に起こる
へのストアとの関係
MAP
で、マップの初期化に関連するすべてのストアを見ることができます。
他のメソッドでは、(2) (スタティック・イニタライザーを使用) と (4) (静的イニタライザーを使用) の両方が、メソッドのセマンティクスを変更するためです。
ファイナル
を設定することができないことを意味します。
MAP
実行時に動的に もし
必要
を宣言してください。
MAP
として
static final HashMap<>
で、安全な出版が保証されます。
実際には、"never-modified object"に安全にアクセスするためのルールは単純です。
でないオブジェクトを公開している場合、そのオブジェクトは
本質的に不変
(と宣言されたすべてのフィールドのように)。
final
) とする。
-
宣言の時点ですでに割り当てられるオブジェクトを作成することができます
a
を使うだけです。
final
フィールドを含むstatic final
静的メンバの場合)。 - 参照が既に可視化された後に、後でオブジェクトを割り当てたい場合: volatileフィールドを使用します。 b .
以上です。
実際に使ってみると、非常に効率的です。を使用することで
static final
フィールドは、例えば、JVMがプログラムの寿命まで値を変更しないと仮定して、大きく最適化することができます。を使うことで
final
メンバーフィールドを使用すると
最も
アーキテクチャでは、通常のフィールド読み出しと同等の方法でフィールドを読み出すことができ、さらなる最適化を阻害することはありません。
c
.
最後に
volatile
多くのアーキテクチャ(x86など、特に読み込みが読み込みを通過することを許さないアーキテクチャ)ではハードウェアバリアは必要ありませんが、コンパイル時にいくつかの最適化と並べ替えが発生しないかもしれません - しかしこの影響は一般的に小さいです。それと引き換えに、あなたは実際に要求した以上のものを手に入れることができます。
HashMap
を保存することができ、さらに多くの修正されていない
HashMap
を同じ参照先に設定すれば、すべての読者が安全に発行されたマップを見ることができることを保証します。
より詳細な情報は、以下を参照してください。 シップイレフ または マンソンとゲッツによるこのFAQ .
[1】直接の引用元 シピレフ .
a 複雑そうに聞こえますが、私が言いたいのは、コンストラクション時に参照を割り当てることができるということです。宣言時点、コンストラクタ(メンバーフィールド)または静的イニシャライザ(静的フィールド)のいずれかにおいてです。
b
オプションで
synchronized
メソッドで取得/設定するか
AtomicReference
などと言われそうですが、最低限の作業で済むという話です。
c メモリモデルが非常に弱い一部のアーキテクチャ(私が見ているのは
あなた
Alpha) の前に何らかの読み取りバリアを必要とする場合があります。
final
を読み取ることができますが、現在では非常にまれです。
関連
-
[解決済み】Javaで文字列をコピーするにはどうしたらいいですか?
-
eclipse で「アクセス制限: タイプ 'HttpServer' は API ではありません」というプロンプトが表示される。
-
eclipseにプロジェクトをインポートした後、Editorにmain typeが含まれない問題
-
Eclipseプロンプトを実行する java仮想マシンを使用しない
-
起動時にEclipseエラーが発生しました。起動中に内部エラーが発生しました。java.lang.NullPoin: "Javaツーリングの初期化 "中に内部エラーが発生しました。
-
Google Chromeのエラー「Not allowed to load local resource」の解決策について
-
[解決済み] Javaで文字列値からenum値を取得する方法
-
[解決済み] Ruby/RailsでHashからキーを削除して残りのHashを取得する方法は?
-
[解決済み] Java Hashmap。値からキーを取得する方法は?
-
[解決済み] ConcurrentHashMapの値の反復はスレッドセーフか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
この行に複数のマーカーがある - HttpServletResponseが型エラーに解決できない
-
Android Studio 3.1.2 で v4, v7 パッケージが見つからない シンボル 'AppCompatActivity' を解決できない
-
JavaMailのメール送信が失敗するケースとその説明の分析
-
Eclipseでプロジェクトエクスプローラービューとパッケージエクスプローラービューを使う
-
エラーの解決方法 jarfile XXX.jarにアクセスできません。
-
Spring BootのテストメソッドFailed to load ApplicationContextの問題を解決する
-
spring-boot 401 このリソースにアクセスするには完全な認証が必要です エラー解決
-
Error: java.lang.NoClassDefFoundError: クラス XXXX を初期化できませんでした
-
JSPで「リストが型解決できない!」の解決方法
-
Java(1)仕上げの基本概念+eclipseのインストール構成