一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して7日目です。クリックしてイベントの詳細をご覧ください。
軽量ロックアップグレード重量ロック
- 1つのスレッドだけがJVMを取得する場合のJVMのバイアスロック
- スレッドが現れると、それは軽量のロックです。軽量ロックはCASを介してロックされます。失敗してスピンする
- スピンがある程度大きい場合や、この時点で別の糸がロックされている場合は、ヘビーウェイトロックに切り替わります。
class Heavy{
}
public class HeavyLock {
public static void main(String[] args) throws InterruptedException {
Heavy heavy = new Heavy();
final Thread t1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
synchronized (heavy) {
System.out.println("t1:"+ClassLayout.parseInstance(heavy).toPrintable());
}
TimeUnit.SECONDS.sleep(1000000);
}
});
final Thread t2 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
synchronized (heavy) {
System.out.println("t2"+ClassLayout.parseInstance(heavy).toPrintable());
}
TimeUnit.SECONDS.sleep(1000000);
}
});
final Thread t3 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
synchronized (heavy) {
System.out.println("t3"+ClassLayout.parseInstance(heavy).toPrintable());
}
TimeUnit.SECONDS.sleep(1000000);
}
});
t1.start();
TimeUnit.SECONDS.sleep(2);
t2.start();
t3.start();
TimeUnit.SECONDS.sleep(5);
System.out.println(ClassLayout.parseInstance(heavy).toPrintable());
}
}
复制代码
- 2つのスレッドが軽量ロックをめぐって競合すると、それらは重量ロックに切り替わることがわかります。
- これらはすべてヘビーウェイトロックですが、他の情報が修正されていることがわかりました。これはメモリレイアウトと同じです。ここでは、C++のObjectMonitorオブジェクトポインタに対応するロックポインタを指しています。ヘビーウェイトポインタ内にキューがあり、これは、取得されていないスレッドを一時停止するために使用されます。したがって、ここでは変更はありません。スレッドIDと区別するためにここに注意してください
- ヘビーウェイトロックが使い果たされると、ロックフリー状態に解放されます。
ロック操作
-
上記では、バイアスロック、軽量ロック、重量ロック、シーンスイッチングの定義を紹介しました。ここでは、3つのロック使用シナリオを簡単に要約します
-
バイアスされたロックは、JVMがバイアス条件を開いたときのデフォルトのバイアスされたロックです。
-
別のスレッドがオブジェクトを再度ロックすると、競合の有無に関係なく軽量ロックになります。ただ、競争が発生するとCASに取って代わられ、CASが一定回数失敗すると、ヘビーウェイトロックがアップグレードされます。バイアスロックを軽量ロックに切り替えるプロセスは、最初に軽量ロックのバイアスロックを取り消すことです。
-
2つ以上の競技がある場合は、ヘビー級ロックに切り替わります。切り替える前に回転して待機しているスレッドがあります。デフォルトは10回です。適応スピンロックが存在するため、これは修正されていません
-
ロック間の関係に加えて、各ロックの原則、ロックとロック解除のプロセス全体を習得する必要もあります。
-
バイアスロック
ロックされています
- まず、下位ステータスビットがtrueであるかどうかを確認します
101
。現在のマークワードに格納されているthreadIdが現在のスレッドであるかどうかを確認している場合。現在のスレッドの場合、現在のコードブロックを実行できます - 現在のスレッドでない場合は、CASを介して現在のスレッドに書き込みます。書き込みが成功すると、バイアスロックが正常にロックされ、現在のコードブロックが続行されます。
- CAS書き込みが失敗した場合、リソースのプリエンプションがあります。バイアスロックは軽量ロックにアップグレードされます。
- まず、下位ステータスビットがtrueであるかどうかを確認します
### 解锁
- 上面的代码案列中,我也有所提到偏向锁是不会主动释放的。因为我们在还没加锁的时候默认就是偏向锁。只有发生竞争了升级轻量级锁的时候才会撤销偏向锁。
- 所以说偏向锁的解锁就是不操作。我们这里主要说下在升级轻量级锁是关于偏向锁的撤销的逻辑
复制代码
## 轻量级锁
### 上锁
- 轻量级锁上锁过程需要借助一个`Lock Record` ; 他是存储在线程栈帧中的一块内存地址。
复制代码
- 因为偏向锁不需要释放,他的可重入式锁就是不做任何操作。但是在轻量级锁中涉及到释放锁。那么轻量级锁如何体现可重入式呢?[这里参考下这篇文章](https://dandelioncloud.cn/article/details/1403089140002131970);
- 在上图中如果是无锁或者偏向锁会在线程栈中开辟LockRecord来存储markword的地址叫做`Displaced MardWord`
- 但是在已经是偏向锁的条件分支里,我没有体现可重入式的概念。在这条分支线里实际上是检测到markword中指向的是当前栈帧的时候JVM还是会开辟一个`Lock Record`,也不会写回到markword中。`LockRecord`不会存储原本markword的内容。此时的`LockRecord`本身就是一个计数器的功能。在释放重入式锁的时候也只是删除`LockRecord`而不会去操作markword。
- 为什么JVM这样设计呢?因为这样就避免了每次轻量级锁的开销
### 解锁
- 对于轻量级锁,其性能提升的依据是“对于绝大部分的锁,在整个生命周期内都是不会存在竞争的”,如果打破这个依据则除了互斥的开销外,还有额外的CAS操作,因此在有多线程竞争的情况下,轻量级锁比重量级锁更慢;
复制代码
要約する
synchronized
批判されたデザインとして、JDK1.6以降に大幅に改訂されました。以前と同じように見ないでください。結局のところ、バイアスロックと軽量ロックの導入により、パフォーマンスが大幅に向上しました。- 偏向锁默认进行,节省调度时间
- 轻量级锁通过CAS完成等待,省调线程挂起,唤醒等操作
- 重量级将线程串行化,保障了线程之间的并发
- 三种状态锁循序渐进给我们适配不同程度的并发需求