Synchronized锁升级之重量级锁

  • Post author:
  • Post category:其他




1.什么是重量级锁

线程之间存在

实际的锁竞争行为

,线程间都有获取锁的需求,但是时间不可交错,互斥锁的等待

大白话就是:在同一时刻存在两个即两个以上线程对同一把锁产生竞争行为

在这里插入图片描述



2.重量级锁的案例

  1. Java case
public class Main {
    public static void main(String[] args) throws InterruptedException {

        Thread.sleep(5000);

        Object objLock = new Object();
        System.out.println(Thread.currentThread().getName() + " - " + ClassLayout.parseInstance(objLock).toPrintable());

        new Thread(() -> {
            synchronized (objLock){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + " - " + ClassLayout.parseInstance(objLock).toPrintable());
        },"T1").start();

        Thread.sleep(1000);

        new Thread(() ->{
            synchronized (objLock){
                System.out.println(Thread.currentThread().getName() + " - " + ClassLayout.parseInstance(objLock).toPrintable());
            }
        },"T2").start();

        new Thread(() ->{
            synchronized (objLock){
                System.out.println(Thread.currentThread().getName() + " - " + ClassLayout.parseInstance(objLock).toPrintable());
            }
        },"T3").start();
    }
}
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
main - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

T1 - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 30 41 d0 (00000101 00110000 01000001 11010000) (-801034235)
      4     4        (object header)                           ff 7e 00 00 (11111111 01111110 00000000 00000000) (32511)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

T2 - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           ca 62 00 8c (11001010 01100010 00000000 10001100) (-1946131766)
      4     4        (object header)                           ff 7e 00 00 (11111111 01111110 00000000 00000000) (32511)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

T3 - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           ca 62 00 8c (11001010 01100010 00000000 10001100) (-1946131766)
      4     4        (object header)                           ff 7e 00 00 (11111111 01111110 00000000 00000000) (32511)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

结合代码与输出结构进行分析

  1. 首先是在执行代码前对主线程(mian)进行了5s的阻塞,这样的目的是为了让偏向锁加载成功,到后面线程T1去获取锁对象的时候,此时的锁就是一个偏向锁
  2. 在T1线程的同步代码块中对T1线程进行了1s的阻塞,用来模拟线程持有锁,但是持有时间并不长
  3. 在创建T2线程前对主线程进行了1s的阻塞,这里是为了避免在同一时刻有超过一个线程对同一把锁的竞争,确保这里一定是T1先获取到锁(避免锁的膨胀导致锁最终变成重量级锁)
  4. 当创建T2线程获取锁的时候T1线程恰好使用完了锁,并释放了锁,此时T2线程会尝试通过CAS去修改Mark Word中的偏向线程ID,此时修改偏向线程ID失败,导致锁从偏向锁升级到轻量级锁,T2获取到锁执行同步代码
  5. 在同一时刻T3线程也发出了获取锁的申请,此时锁已经被T2线程占用并还未释放,此时T3会进行自旋重复的尝试获取锁,当自旋次数达到10次时还未申请到锁,此时锁将会进行膨胀升级为重量级锁(防止CPU空转)T2线程继续执行同步代码块,执行完成后释放锁,T3获取到锁执行同步代码块



版权声明:本文为ScholarTang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。