ホームページ  >  記事  >  運用・保守  >  JAVA言語でダブルチェックロックを解析する方法

JAVA言語でダブルチェックロックを解析する方法

王林
王林転載
2023-05-12 08:55:171242ブラウズ

1. ロックの二重チェック

プログラム開発では、高コストのオブジェクトの初期化操作を延期し、これらのオブジェクトが使用されるときにのみ初期化する必要がある場合があります。を使用すると、二重チェック ロックを使用してオブジェクトの初期化操作を遅らせることができます。ダブルチェック ロックは、同時実行システムにおける競合と同期のオーバーヘッドを軽減するために設計されたソフトウェア設計パターンです。通常のシングルトン パターンに基づいて、最初にオブジェクトが初期化されているかどうかを判断し、次にそれをロックするかどうかを決定します。二重チェックされたロックは、マルチスレッド環境における通常のシングルトン パターンのエラーが発生しやすくスレッドセーフでない問題を解決しますが、依然としていくつかの隠れた危険性があります。以下では、JAVA 言語のソース コードを例として、ダブルチェック ロックの欠陥の原因と修復方法を分析します。

2. ダブル チェック ロック危険

ダブル チェック ロックは、シングル スレッド環境では影響しません。スレッドはいつでも実行を切り替えるため、命令が再配置されるとオブジェクトが完全にインスタンス化されず、プログラム呼び出しエラーが発生します。

3. サンプル コード

この例は、Samate Juliet Test Suite for Java v1.3 (https://samate.nist.gov/SARD/testsuite. php )、ソース ファイル名: CWE609_Double_Checked_Locking__Servlet_01.java。

3.1 欠陥コード

JAVA言語でダブルチェックロックを解析する方法

上記のコード行は 23 ~ 38 です。プログラムはまず、stringBad## かどうかを判断します。 # null の場合、そうでない場合は、String オブジェクトが直接返されるため、synchronized ブロックに入るために必要なリソースが回避されます。 synchronized キーワードを使用すると、stringBad が null の場合にマルチスレッド環境で String オブジェクトが複数作成されることを回避できます。実際にコードを実行すると、上記のコードでもエラーが発生する可能性があります。

行 33 では、

stringBad オブジェクトの作成と代入操作が 2 つのステップで実行されます。ただし、JVM はこれら 2 つの操作の順序を保証しません。命令が並べ替えられると、JVM はまずメモリ アドレスを指す値を割り当て、次に stringBad オブジェクトを初期化します。この時点で 2 つのスレッドがある場合、両方のスレッドが同時に 27 行目に入ります。スレッド 1 は最初に synchronized ブロックに入り、stringBad が null であるため、33 行目を実行します。 JVM が命令を並べ替えるとき、JVM はまずインスタンスの空のメモリを割り当てて stringBad に割り当てますが、この時点では stringBad オブジェクトはまだインスタンス化されていません。スレッド 1 は synchronized ブロックを残しました。スレッド 2 が synchronized ブロックに入ると、この時点では stringBad が null ではないため、インスタンス化されていないオブジェクトが直接返されます (メモリ アドレス値のみで、オブジェクトは実際には初期化されません)。後続のスレッド 2 が stringBad オブジェクトを操作するプログラムを呼び出すと、この時点のオブジェクトは初期化されていないため、エラーが発生します。

360 コード ガードを使用して上記のサンプル コードを検出すると、「ダブル チェック ロック」の欠陥が検出でき、表示レベルは中です。図 1 に示すように、この欠陥はコードの 27 行目で報告されます。


JAVA言語でダブルチェックロックを解析する方法#図 1: 「Double Check Lock」の検出例

3.2 コードを修正

#

上記の修復コードでは、23 行目の volatile キーワードを使用して、シングルトン変数 stringBad を変更します。 volatile 命令キーワードとして、コンパイラの最適化によって命令が省略されないようにし、毎回値を直接読み取る必要があります。

コンパイラの最適化により、コードの実際の実行は、記述した順序と異なる場合があります。コンパイラは、プログラムの実行結果がソースコードと同じであることを保証するだけで、実際の命令の順序がソースコードと同じであることは保証しないため、シングルスレッド環境では問題ありません。マルチスレッド環境を導入すると、この種の障害が深刻な問題を引き起こす可能性があります。 volatile キーワードはこの問題を意味的に解決できますが、volatile の命令並べ替え最適化機能の禁止は Java 1.5 でのみ実装されたため、1.5 より前のバージョンはまだ残っていることに注意してください。 volatile キーワードが使用されている場合でも、安全ではありません。

360 コード ガードを使用して修復されたコードを検出すると、「ダブル チェック ロック」欠陥が存在しないことがわかります。図 2 に示すように:


JAVA言語でダブルチェックロックを解析する方法

# 図 2: 修理後の検出結果

4、二重チェック ロックを回避する方法

二重チェック ロックを回避するには、次の点に注意する必要があります。

(1) volatile キーワードを使用して命令の並べ替えを回避しますが、このソリューションには JDK5 以降が必要です。これは、JDK5 から使用されるためです。新しい JSR-133 メモリ モデル仕様は、volatile のセマンティクスを強化します。

(2) クラスの初期化に基づく解決策。

JVM は、クラスの初期化フェーズ中 (つまり、クラスがロードされた後、スレッドによって使用される前) にクラスの初期化を実行します。実行クラスの初期化中に、JVM はロックを取得します。このロックは、複数のスレッドによる同じクラスの初期化を同期できます。 JAVA言語でダブルチェックロックを解析する方法

以上がJAVA言語でダブルチェックロックを解析する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。