JDK 1.5 では、synchronized は実装するためにモニター ロック (Monitor) を呼び出す必要があり、モニター ロックは基本的に基礎となる層に依存します。 OSのミューテックスロック(ミューテックスロック)によって実現されており、ミューテックスロックの解放や取得の際にはユーザーモードからカーネルモードへの変換が必要となるため、コストが高く、実行時間も長くなります。オペレーティング システムのミューテックス ロック実装に依存するものは、「ヘビーウェイト ロック」と呼ばれます。
ユーザー モード: プロセスがユーザー独自のコードを実行しているとき、プロセスはユーザー実行状態であると言われます。カーネル モード: タスク (プロセス) がシステム コールを実行し、カーネル コードにトラップされるとき、プロセスはカーネル実行状態にあると言います。このとき、プロセッサは最高の特権レベルでカーネル コードを実行しています。 。
カーネル モードとユーザー モードの区別がないと仮定すると、プログラムは、メモリの読み取り、書き込み、割り当てなど、ハードウェア リソースを自由に読み書きできます。不適切な内容を記載すべきでない場所に書き込むと、システムがクラッシュする可能性があります。
プログラムはユーザーモードとカーネルモードを区別して動作を行う際に一連の検証・検査を行い、問題がないことを確認して初めてリソースを正常に動作させることができます。誤ってシステムに損傷を与える心配はありません。つまり、カーネル モードとユーザー モードを区別することで、プログラムをより安全に実行できますが、同時に、2 つのモードを切り替えると、一定のパフォーマンスのオーバーヘッドが発生します。
JDK 1.6 では、ロックの取得と解放によるパフォーマンスの消費を解決するために、「バイアス ロック」と「軽量ロック」の状態が導入されました。現時点では、合計 4 つの同期状態があります:
ロックなし
バイアスされたロック
軽量ロック
重量ロック
ロックのレベルは上記の順番でアップグレードされますので、これをアップグレードしましょう。このプロセスは「ロック拡張」と呼ばれます。
PS: これまでのところ、ロックのアップグレードは一方向です。つまり、低から高へのみアップグレードできます (いいえ)ロック -> バイアス ロック -> 軽量ロック -> 重量ロック)、ロックの劣化はありません。
ロック拡張により同期のパフォーマンスが最適化されるのはなぜですか?これらのロック状態を理解すれば、自ずと答えが見えてきますので、一緒に見ていきましょう。
HotSpot 著者は研究と実践を通じて、ほとんどの場合、ロックをめぐるマルチスレッドの競合はなく、ロックは常に同じスレッドによって複数回取得されることを発見しました。スレッドがロックを取得できるようにするため、コストが低かったため、バイアス ロックが導入されました。
バイアス ロックとは、ロックにアクセスする最初のスレッドにバイアスがかかることを意味します。動作中に 1 つのスレッドだけが同期ロックにアクセスする場合、マルチスレッドの競合は発生しません。その場合、スレッドはロックを行う必要がありません。トリガー同期 この場合、バイアス ロックがスレッドに追加されます。
スレッドが同期されたコード ブロックにアクセスしてロックを取得すると、ロック バイアスのスレッド ID がオブジェクト ヘッダーのマーク ワードに格納され、スレッド ID は、スレッドが同期ブロックに出入りするときにオブジェクト ヘッダーのマーク ワードに保存されます。CAS 操作によるロックとロック解除の代わりに、現在のスレッドを指すバイアス ロックがマーク ワードに保存されているかどうかを検出します。 Mark Word のスレッド ID がアクセスされたスレッド ID と一致している場合は、コードに同期ブロックを直接入力できます。実行、スレッド ID が異なる場合は、CAS を使用してロックの取得を試みます。取得が成功した場合は、同期ブロックを使用してコードを実行します。それ以外の場合、ロック ステータスは軽量ロックにアップグレードされます。
バイアス ロックは、ロックの取得と解放が複数回に依存するため、マルチスレッドの競合がなく、不必要なロックの切り替えを最小限に抑えるように設計されています。CAS アトミック命令とバイアス ロックスレッド ID を置き換えるときに CAS アトミック命令を 1 回実行するだけで済みます。
HotSpot 仮想マシンでは、メモリに格納されているオブジェクトのレイアウトは次の 3 つの領域に分割できます。
オブジェクトヘッダー#インスタンスデータ
この領域に保存されます。
JDK 1.6 では、バイアス ロックはデフォルトで有効になっていますが、「-XX:-UseBiasedLocking=false」コマンドを使用してバイアス ロックを無効にできます。
軽量ロックを導入する目的は、マルチスレッドの競合を行わずに、オペレーティング システムにおける従来の重量ロックである Mutex Lock (ミューテックス ロック) の使用を減らすことです。ロックによる消費 。 Mutex Lock を使用する場合、ロックの取得と解放の各操作でユーザー モードとカーネル モードが切り替わり、システムに多大なパフォーマンスのオーバーヘッドが発生します。
バイアス ロックがオフになっている場合、または複数のスレッドがバイアス ロックを競合している場合、バイアス ロックは軽量ロックにアップグレードされます。軽量ロックの取得と解放は CAS を通じて完了し、ロックの取得は完了するまでに特定の回数のスピンが必要になる場合があります。
強調する必要があります: 軽量ロックは、重量ロックを置き換えるために使用されるものではありません. 本来の目的は、マルチスレッドの競合なしでそれらを使用することです。従来の重量ロックの使用によって生じるパフォーマンスの消費。軽量ロックが適応するシナリオは、スレッドが同期ブロックを交互に実行する状況であり、複数のスレッドが同時にアクセスすると、軽量ロックは重量ロックに拡張されます。
synchronized は、メソッド同期またはコード ブロック同期を実装するためにモニターに依存します。コード ブロック同期は、monitorenter 命令とmonitorexit 命令を使用して実装されます。monitorenter 命令は、次の場所に挿入された後にコンパイルされます。同期されたコード ブロックの先頭に、monitorexit がメソッドと例外の最後に挿入されます。すべてのオブジェクトにはそれに関連付けられたモニターがあります。モニターが保持されると、ロック状態になります。
ロック コードは次のとおりです:
public class SynchronizedToMonitorExample { public static void main(String[] args) { int count = 0; synchronized (SynchronizedToMonitorExample.class) { for (int i = 0; i < 10; i++) { count++; } } System.out.println(count); } }
上記のコードをバイトコードにコンパイルすると、その内容は次のようになります:
上記の結果から、main メソッドの実行中に、monitorenter 命令とmonitorexit 命令が複数存在することがわかります。Monitor モニターに依存して synchronized が実装されていることがわかります。モニター ロックはオペレーティング システムのミューテックス ロックに依存しています。ミューテックス ロックにより、ロックの取得と解放のたびにユーザー モードとカーネル モードが切り替わり、システムのパフォーマンスのオーバーヘッドが増加します。
以上がJava で同期ロック拡張メカニズムを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。