Javaにはスレッドのセキュリティを保証するメカニズムがありますか?synchronizedキーワードとvolatileキーワード
3317 ワード
スレッドセキュリティの問題を解決するには、まずなぜスレッドが安全ではないのかを知る必要があります.単一スレッドでは、スレッドセキュリティの問題については言及していません.スレッドセキュリティの問題は、マルチスレッドにのみ発生する問題です.マルチスレッドの場合は共有データがあるため、各スレッドはこれらのデータを共有し、これらのデータを修正し、データを修正して書き込む必要がある場合、スレッドスケジューリングのクロス実行、メモリモデルの原因で不安全な問題が発生します.
スレッドの安全を保証することは難しいことですが、一般的には(3つのコア)1.原子性2.可視性3.再ソート問題はまず、原子性、可視性、再ソート問題とは何かを説明します.原子性、再分割できないコードセグメントは、すべて実行するか、実行しないかのいずれかです.(1つのコードは必ずしも原子ではありません.例えばa=bで、bをメインメモリからワークメモリにロードし、bの値をaに割り当て、最後にaをメインメモリに保存するのは明らかに3つのステップです.)可視性で、すべてのデータはメインメモリに存在し、各スレッドには独自のワークメモリがあり、作業時にメインメモリからワークメモリにデータをロードします.(これは実行効率の向上に役立ちます)、この場合、本スレッドがデータを修正した場合、他のスレッドがタイムリーに見られるようになります(ただし、本スレッドがデータを修正した場合が多く、他のスレッドで古いデータを使用している場合があります.これはエラーが発生し、安全ではありません.)並べ替え問題は、単一スレッドでは順番に実行されますが、マルチスレッドではCPU/コンパイラ/JITがコードの実行順序を一定の調整(より適切な方法で実行、最適化)しますが、結果の正確性が保証されず、エラーが発生します.これがコード並べ替え問題です.
これらの不安全な問題を解決するためにsynchronizedとvolatileのキーワードが発生しました.synchronizedとvolatileがどのように安全性を保証しているかを見て、自分で書いたコードのスレッドの安全を保証する方法を学びました.
1.synchronizedまずsynchronizedの文法を学ぶ:1)メソッド修飾子--他の修飾子の使用と似ている2)コードブロック--コードブロックとして現れる次に、synchronizedがどのようにスレッドの安全を保証するかについて話します.synchronizedはオブジェクトにロックをかけることで、原子性と可視性を保証してスレッドを安全にします.
まず一般的な方法にロックをかけます.ロックはスタックに作成されたオブジェクトに加えられます.スレッドがCPUをプリエンプトすることに成功したとき、そのオブジェクトをチェックします.ロックが開いていることを発見しました(ロックの初期状態では開いています).このオブジェクトにロックをかけて、方法の実行が終了するまで(正常/異常)、ロックを解放します.現在のスレッドに実行コードをロックする過程で、必ずしもCPUをプリエンプトできるとは限らない.もし自分のタイムスライスが切れたり、CPUを自発的に放棄したりしたら、スレッドは他のスレッドにスケジューリングされ、他のスレッドはこの時にCPUをプリエンプトすることに成功した.この時、そのオブジェクト(注意:前と同じオブジェクト、現在のオブジェクト、thisが指しているオブジェクト)がロックされていることを発見し、このとき,スレッドは直接ブロックキューに入れられ,このロックが解放された後にのみ準備キューに入り,CPUをプリエンプトする機会が得られ,各ロックには対応するブロックキューがある.さらに静的メソッドにロックをかけ,静的メソッドにロックをかけるのは,クラスを置くメソッド領域に,クラスのメタオブジェクトに加算される.
synchronizedのコード表現:
|表現--------------|ロックの対象-------|いつロックをかけるか-------|いつ解放するか-||修飾普通方法|this-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ロックは原子性を保証し,ある程度可視性を保証した.ロックをかけると、すべてのスレッドが現在のデータを同期し、ロックを解除すると、データは再び同期しますが、ロック解除というコードの実行中に可視性は保証されません.ロックされた内部のコードは並べ替えられ、外部のコードは並べ替えられますが、内部と外部の間には通じないコード実行順序を交換することはできません.
synchronizedを使用する欠点:理論的にすべてのスレッドセキュリティ問題はsynchronizedを使用して解決できるが、コストは非常に高い.(スレッドスケジューリングを継続すること自体に多くの時間がかかります.また、毎回同じスレッドがCPUをプリエンプトすることに成功すると、他のスレッドは常にブロック・プリエンプトの過程にあります)
synchronizedキーワードを使用する場合は、できるだけ適切な位置にロックする必要があります.範囲が大きすぎると、ロックの粒度の問題、粗粒度、細粒度に注目する意味がありません.
2.volatile構文:修飾変数
この変数の可視性を保証できる問題(後述しない)は、コードの再ソート問題(オブジェクト初期化、new、オブジェクト初期化、オブジェクト付与参照の3つのステップに分けられる.volatileは、この3つのステップが順次実行されることを保証する)を部分的に保証することができる.
原子性に関する1つのまとめ:基本データ型ではbyte、int、Boolean、float、char、shortが字面量として原子であり、変数として原子ではない.long、doubleはいつでも原子ではありません.(いずれも64ビットの32ビットマシン運転時に低32ビットと高32ビットに分かれているため、一歩では実行されません)
スレッドの安全を保証することは難しいことですが、一般的には(3つのコア)1.原子性2.可視性3.再ソート問題はまず、原子性、可視性、再ソート問題とは何かを説明します.原子性、再分割できないコードセグメントは、すべて実行するか、実行しないかのいずれかです.(1つのコードは必ずしも原子ではありません.例えばa=bで、bをメインメモリからワークメモリにロードし、bの値をaに割り当て、最後にaをメインメモリに保存するのは明らかに3つのステップです.)可視性で、すべてのデータはメインメモリに存在し、各スレッドには独自のワークメモリがあり、作業時にメインメモリからワークメモリにデータをロードします.(これは実行効率の向上に役立ちます)、この場合、本スレッドがデータを修正した場合、他のスレッドがタイムリーに見られるようになります(ただし、本スレッドがデータを修正した場合が多く、他のスレッドで古いデータを使用している場合があります.これはエラーが発生し、安全ではありません.)並べ替え問題は、単一スレッドでは順番に実行されますが、マルチスレッドではCPU/コンパイラ/JITがコードの実行順序を一定の調整(より適切な方法で実行、最適化)しますが、結果の正確性が保証されず、エラーが発生します.これがコード並べ替え問題です.
これらの不安全な問題を解決するためにsynchronizedとvolatileのキーワードが発生しました.synchronizedとvolatileがどのように安全性を保証しているかを見て、自分で書いたコードのスレッドの安全を保証する方法を学びました.
1.synchronizedまずsynchronizedの文法を学ぶ:1)メソッド修飾子--他の修飾子の使用と似ている2)コードブロック--コードブロックとして現れる次に、synchronizedがどのようにスレッドの安全を保証するかについて話します.synchronizedはオブジェクトにロックをかけることで、原子性と可視性を保証してスレッドを安全にします.
まず一般的な方法にロックをかけます.ロックはスタックに作成されたオブジェクトに加えられます.スレッドがCPUをプリエンプトすることに成功したとき、そのオブジェクトをチェックします.ロックが開いていることを発見しました(ロックの初期状態では開いています).このオブジェクトにロックをかけて、方法の実行が終了するまで(正常/異常)、ロックを解放します.現在のスレッドに実行コードをロックする過程で、必ずしもCPUをプリエンプトできるとは限らない.もし自分のタイムスライスが切れたり、CPUを自発的に放棄したりしたら、スレッドは他のスレッドにスケジューリングされ、他のスレッドはこの時にCPUをプリエンプトすることに成功した.この時、そのオブジェクト(注意:前と同じオブジェクト、現在のオブジェクト、thisが指しているオブジェクト)がロックされていることを発見し、このとき,スレッドは直接ブロックキューに入れられ,このロックが解放された後にのみ準備キューに入り,CPUをプリエンプトする機会が得られ,各ロックには対応するブロックキューがある.さらに静的メソッドにロックをかけ,静的メソッドにロックをかけるのは,クラスを置くメソッド領域に,クラスのメタオブジェクトに加算される.
synchronizedのコード表現:
|表現--------------|ロックの対象-------|いつロックをかけるか-------|いつ解放するか-||修飾普通方法|this-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ロックは原子性を保証し,ある程度可視性を保証した.ロックをかけると、すべてのスレッドが現在のデータを同期し、ロックを解除すると、データは再び同期しますが、ロック解除というコードの実行中に可視性は保証されません.ロックされた内部のコードは並べ替えられ、外部のコードは並べ替えられますが、内部と外部の間には通じないコード実行順序を交換することはできません.
synchronizedを使用する欠点:理論的にすべてのスレッドセキュリティ問題はsynchronizedを使用して解決できるが、コストは非常に高い.(スレッドスケジューリングを継続すること自体に多くの時間がかかります.また、毎回同じスレッドがCPUをプリエンプトすることに成功すると、他のスレッドは常にブロック・プリエンプトの過程にあります)
synchronizedキーワードを使用する場合は、できるだけ適切な位置にロックする必要があります.範囲が大きすぎると、ロックの粒度の問題、粗粒度、細粒度に注目する意味がありません.
2.volatile構文:修飾変数
この変数の可視性を保証できる問題(後述しない)は、コードの再ソート問題(オブジェクト初期化、new、オブジェクト初期化、オブジェクト付与参照の3つのステップに分けられる.volatileは、この3つのステップが順次実行されることを保証する)を部分的に保証することができる.
原子性に関する1つのまとめ:基本データ型ではbyte、int、Boolean、float、char、shortが字面量として原子であり、変数として原子ではない.long、doubleはいつでも原子ではありません.(いずれも64ビットの32ビットマシン運転時に低32ビットと高32ビットに分かれているため、一歩では実行されません)
: : ( ), )