ロックの最適化
ここでのロックの最適化とは主にJVMによる同期の最適化を指します。
スピンロック
ブロッキング状態へのミューテックス同期は非常にコストがかかるため、できるだけ避ける必要があります。多くのアプリケーションでは、共有データは短期間のみロックされます。スピン ロックの概念は、共有データ ロックを要求するときに、スレッドが一定期間ビジー ループ (スピン) を実行できるようにすることです。この期間中にロックを取得できれば、ブロック状態に入るのを回避できます。 。
スピン ロックはブロッキング状態への移行を回避し、オーバーヘッドを削減できますが、CPU 時間を占有するビジー ループ操作が必要となるため、共有データのロック状態が非常に短いシナリオにのみ適しています。
アダプティブ スピン ロックは JDK 1.6 で導入されました。アダプティブとは、スピン数が固定されなくなり、同じロック上の以前のスピン数とロック所有者のステータスによって決定されることを意味します。
ロックの削除
ロックの削除とは、競合の可能性が低いと検出された共有データのロックを削除することを指します。
ロックの削除は主にエスケープ分析によってサポートされており、ヒープ上の共有データがエスケープできず、他のスレッドからアクセスできない場合、それらのデータをプライベート データとして扱い、そのロックを削除できます。
ロックされていないように見える一部のコードでは、実際には多くのロックが暗黙的に追加されています。たとえば、次の文字列結合コードは暗黙的にロックを追加します:
public static String concatString(String s1, String s2, String s3) { return s1 s2 s3; }
String は不変クラスであり、コンパイラは String の結合を自動的に最適化します。 JDK 1.5 より前では、連続的な append() 操作は StringBuffer オブジェクトに変換されます:
public static String concatString(String s1, String s2, String s3) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); sb.append(s3); return sb.toString() ; }
すべての append() メソッドには同期ブロックがあります。仮想マシンは変数 sb を監視し、その動的スコープが concatString() メソッド内に制限されていることをすぐに発見します。つまり、sb への参照は concatString() メソッドの外にエスケープされることはなく、他のスレッドからアクセスできないため、削除できます。
ロックの荒らし処理
一連の連続した操作で同じオブジェクトのロックとロック解除が繰り返される場合、ロック操作が頻繁に行われるとパフォーマンスが低下します。
前のセクションのサンプル コード内の連続する append() メソッドは、このカテゴリに分類されます。仮想マシンは、このような断片化された一連の操作によって同じオブジェクトがロックされていることを検出すると、ロック範囲を操作シーケンス全体の外側に拡張します (粗くします)。前のセクションのサンプル コードは、最初の append() 操作の前から最後の append() 操作の後まで拡張されているため、ロックする必要があるのは 1 回だけです。
軽量ロック
JDK 1.6 では、バイアス ロックと軽量ロックが導入され、ロックがロック解除、バイアス、軽量ロック、重量ロックの 4 つの状態を持つことができるようになりました。
重量ロックは、一般に同期オブジェクト ロックとも呼ばれます。
以下は HotSpot 仮想マシン オブジェクト ヘッダーのメモリ レイアウトであり、これらのデータはマーク ワードと呼ばれます。タグ ビットは 5 つの状態に対応しており、右側の状態テーブルに示されています。 gc 状態のマークに加えて、他の 4 つの状態は以前に導入されました。
下図の左側はスレッドの仮想マシンスタックで、軽量ロックの実行処理中に作成されるロックレコードと呼ばれる領域の一部があり、ロックオブジェクトのマークワードを格納するために使用されます。右側にはロック オブジェクトがあり、マーク ワードとその他の情報が含まれています。
従来の重量ロックと比較して、軽量ロックは CAS 操作を使用して、ミューテックスを使用する重量ロックのオーバーヘッドを回避します。ほとんどのロックでは、同期サイクル全体で競合が発生しないため、同期にミューテックスを使用する必要はありません。同期には、最初に CAS 操作を使用できます。CAS が失敗した場合は、代わりにミューテックスを使用して同期します。
ロック オブジェクトを取得しようとしたときに、ロック オブジェクトが 0 01 とマークされている場合、ロック オブジェクトがロック解除状態にあることを意味します。このとき、仮想マシンは現在のスレッドの仮想マシン スタックにロック レコードを作成し、CAS 操作を使用してオブジェクトのマーク ワードをロック レコード ポインタに更新します。 CAS 操作が成功すると、スレッドはオブジェクトのロックを取得し、オブジェクトの Mark Word ロック タグが 00 に変更され、オブジェクトが軽量ロック状態にあることを示します。
CAS 操作が失敗した場合、仮想マシンは最初にオブジェクトのマーク ワードが現在のスレッドの仮想マシン スタックをポイントしているかどうかを確認します。そうであれば、現在のスレッドがすでにロック オブジェクトを所有していることを意味し、その後、ロック オブジェクトに直接入ることができます。それ以外の場合は、ロック オブジェクトが他のスレッドによってプリエンプトされたことを意味します。 3 つ以上のスレッドが同じロックをめぐって競合する場合、軽量ロックは効果がなくなるため、重量ロックに拡張する必要があります。
バイアスロック
バイアス ロックの考え方は、ロック オブジェクトを取得する最初のスレッドを優先することです。このスレッドは、ロックを取得した後に同期操作を実行する必要がなくなり、CAS 操作さえも必要なくなります。
ロック オブジェクトがスレッドによって初めて取得されると、バイアス状態になり、1 01 としてマークされます。同時に、CAS 操作を使用して、スレッド ID を Mark Word に記録します。CAS 操作が成功した場合、このスレッドは、このロックに関連する同期ブロックに入るたびに同期操作を実行する必要はありません。
別のスレッドがこのロックオブジェクトを取得しようとすると、バイアス状態は終了しますが、このときバイアス(Revoke Bias)は取り消され、アンロック状態または軽量ロック状態に戻ります。
以上がJava ロックを最適化する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。