四、ロック
1.ロックの4つの状態
[1]ロックの最適化
ロックの取得と解放のパフォーマンスコストを削減するために、JDK6では「バイアスロック」と「軽量ロック」が導入されました。
その結果、ロックには4つの状態があり、ロックの競合によってアップグレードされます。ロックはアップグレードできますが、ダウングレードはできません。ロックのアップグレードは「ロック拡張」と呼ばれます。
[2]部分ロック状態
ほとんどの場合、ロックにはマルチスレッドの競合がないだけでなく、常に同じスレッドによって複数回取得されます
この状況のパフォーマンスを改善するために、バイアスロックが導入されています
ロックバイアスのスレッドIDは、オブジェクトヘッダーとスタックフレームのロックレコードに格納されます。スレッドが同期ブロックに出入りするときに、CAS操作を実行してロックとロックを解除し、そこにあるかどうかを直接判断する必要はありません。オブジェクトヘッダーのマークワードです現在のスレッドのバイアスロックを指します
存在する場合、スレッドはロックを取得しました。存在しない場合、ロックは軽量ロックに拡張されます。
バイアスロックは、JDK6およびJDK7でデフォルトで有効になっていますが、プログラムの開始から数秒後にアクティブにする必要があります。
この遅延をオフにする必要がある場合は、JVMパラメーターを使用します
-XX:BiasedLockingStartupDelay=0
バイアスロックをオフにする場合は、JVMパラメータを使用します
-XX:-UseBiasedLocking
[3]軽量ロックステータス
ロック:JVMは、現在のスレッドのスタックフレームにロックマークを格納するためのスペースを作成し、オブジェクトヘッダーのDisplaced MarkWordをそれにコピーします。成功した場合は、現在のスレッドがロックを正常に取得したことを意味します。失敗した場合は、ロックの競合があることを意味します。現在のスレッドは、スピンを使用してロックを取得しようとします。
ロック解除:アトミックCAS操作を使用して、Displaced MarkWordをオブジェクトヘッドに戻します。成功した場合はロックの競合がないことを意味し、失敗した場合はロックの競合が発生したことを意味し、ロックがヘビーウェイトロックに拡張されます。
[4]コントラスト
ロックタイプ | 利点 | 不利益 | 該当シーン |
---|---|---|---|
バイアスロック | ロックとロック解除を待つ必要はなく 、ロックなしの実行方法と比較してナノ秒の違い(1ナノ秒= 1000 * 1000ミリ秒)があります。 |
ロックの競争がある場合、それ は失効ロックの追加消費をもたらします |
1つのスレッドのみが同期ブロックにアクセスします |
軽量ロック | 競合するスレッドがブロックされないため 、プログラムの応答速度が向上します |
スレッドがロックを取得できなかった場合、 スピンを使用するとCPUが消費されます |
応答速度の追求、 同期ブロックの実行速度は非常に速い |
ヘビー級ロック | スレッドの競合はスピンを使用せず、CPUを消費しません | 競合するスレッドがブロックされ、 応答速度が低下します |
スループットを追求し、同期ブロックの実行時間が長くなります |
2.フェアロックとアンフェアロック
フェアロック:各スレッドがロックを取得すると、最初にロックによって維持されている待機キューをチェックします。キューが空であるか、現在のスレッドがキューの最初(先頭)である場合、スレッドはロックを取得します。将来的には、FIFOの原則に従って待機キューから取得されます(ロックを取得するために待機するキュー)
不公平なロック:すべてのスレッドがロックの取得を試みることができます
同期は不公平なロックです
java.util.concurrent.locks.ReentrantLockは、パラメーターのないコンストラクターによって作成された不公平なロックです。パラメータ化されたコンストラクタを渡し、trueを渡すと、ロックは公正なロックになります
3.リエントラントロック
リエントラントロックは再帰ロックとも呼ばれ、ロックを取得したスレッドを指します。同期メソッドで同期メソッドを実行すると、ロックを再度取得しなくてもメソッドを直接実行できます。
同期およびjava.util.concurrent.locks.ReentrantLockは再入可能ロックです
4.スピンロック
スレッドは、スレッドコンテキスト切り替えのオーバーヘッドを減らすために、スピンによってロックを取得しようとします
class CasLock {
private final AtomicReference<Thread> reference = new AtomicReference<Thread>();
public void lock() {
for (;;) {
if (tryAcquire()) {
break;
}
}
}
public void unlock() {
for (;;) {
if (tryRelease()) {
break;
}
}
}
private boolean tryAcquire() {
Thread thread = Thread.currentThread();
return reference.compareAndSet(null, thread);
}
private boolean tryRelease() {
Thread thread = Thread.currentThread();
return reference.compareAndSet(thread, null);
}
}
5.共有ロックと排他ロック
共有ロック:複数のスレッドがロックを取得できます
排他的ロック:ロックは1つのスレッドにのみ排他的です
java.util.concurrent.locks.ReentrantReadWriteLockの読み取りロックはスレッドによって共有され、書き込みロックはスレッドによって排他的に共有されます
同期およびjava.util.concurrent.locks.ReentrantLockは排他ロックです
6.読み取り/書き込みロック
java.util.concurrent.locks.ReentrantReadWriteLock
読み取り/書き込みロック、読み取りロックと書き込みロックの共存、読み取りロックの共有、書き込みロックの排他的
7.スレッドの互換性とスレッドの反対
スレッドの互換性:操作されるオブジェクトはスレッドセーフではありませんが、さまざまなスレッドセーフの方法を使用して、マルチスレッド環境でオブジェクトがスレッドセーフであることを確認できます。
スレッド反対:操作対象のオブジェクトはスレッドセーフですが、マルチスレッド環境で操作する場合、複数スレッドの同時要求はスレッドセーフではないため、操作オブジェクト自体がスレッドセーフになります。
Javaに敵対する行の例:スレッドクラスの破棄メソッド(@Deprecated)suspend
でresume
あり、デッドロックが発生する可能性があります