Synchronized原理--偏向锁

119 阅读29分钟

偏向锁

轻量锁在没有竞争时(就自己的这个线程),每次重入仍然需要执行CAS操作。Java 6中引入了偏向锁来做进一步优化:只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程 ID 是自己的就表示没有竞争,不用重新CAS操作。以后只要不发生竞争,这个对象就归该线程所有。通俗的讲,偏向锁就是在运行过程中,对象的锁偏向某个线程。即在开启偏向锁机制的情况下,某个线程获得锁,当该线程下次再想要获得锁时,不需要再获得锁(即忽略synchronized关键词),直接就可以执行同步代码,比较适合竞争较少的情况。比如下图:

偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加JVM参数 - XX:BiasedLockingStartupDelay=0来禁用延迟。 添加JVM参数-XX:-UseBiasedLocking关闭偏向锁,就可以直接进入轻量级锁。

static final Object obj = new Object();
public static void m1() {
    synchronized( obj ) {
        // 同步块 A
        m2();
    }
}
public static void m2() {
    synchronized( obj ) {
        // 同步块 B
        m3();
    }
}
public static void m3() {
    synchronized( obj ) {
        // 同步块 C
    }
}

分析如图所示: image.png

偏向状态

对象头格式: image.png 一个对象创建时:

  • 如果开启了偏向锁(默认开启),那么对象创建后,markword值为0x05即最后3位为101,这时它的thread、epoch、age 都为 0
  • 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加VM参数 - XX:BiasedLockingStartupDelay=0来禁用延迟。
  • 如果没有开启偏向锁,那么对象创建后,markword值为0x01即最后3位为001,这时它的 hashcode、age都为 0,第一次用到hashcode时才会赋值。
  • 在添加 VM 参数 -XX:-UseBiasedLocking 可以禁用偏向锁。
public class Test1 {
    //未设置`VM`参数 `-XX:BiasedLockingStartupDelay=0`来禁用偏向锁的延迟。
    public static void main(String[] args) throws InterruptedException {
        System.out.println(ClassLayout.parseInstance(new Teacher()).toPrintable());
        //因为偏向锁有延迟,因此睡眠一会。
        Thread.sleep(4000);
        System.out.println(ClassLayout.parseInstance(new Teacher()).toPrintable());
    }
}

class Teacher{
    int i = 0;
}

//执行结果:
com.wuke.test.Teacher object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0xf800c143
 12   4    int Teacher.i                 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

com.wuke.test.Teacher object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)
  8   4        (object header: class)    0xf800c143
 12   4    int Teacher.i                 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

由以上结果可以看到,偏向锁是有延迟的,未设置VM参数 -XX:BiasedLockingStartupDelay=0来禁用偏向锁的延迟时MarkWordVALUE0x0000000000000001 (non-biasable; age: 0),表示无锁状态 01 。睡眠 2s 后的VALUE0x0000000000000005 (biasable; age: 0)。5(十进制)= 101(二进制),表示偏向锁状态 101 。

撤销偏向锁

撤销情况一:

当调用了锁对象的hashCode时,但偏向锁的对象中存储的是线程ID,如果调用hashCode会导致偏向锁被撤销。

  • 轻量级锁会在线程栈帧的所记录中记录hashCode
  • 重量级锁则会在Monitor中记录hashCode
@Slf4j
public class Test {

    //记得取消虚拟机参数 -XX:BiasedLockingStartupDelay=0(取消偏向锁延迟)
    public static void main(String[] args) throws IOException {
        Student student = new Student();
        //调用该对象的hashCode的时候会禁用掉这个对象的偏向锁,直接升级为轻量级锁。因为偏向锁的对象中存储的是线程ID,不是hashCode。
        student.hashCode();
        log.info(ClassLayout.parseInstance(student).toPrintable());
        synchronized (student) {
            log.info(ClassLayout.parseInstance(student).toPrintable());
        }
        log.info(ClassLayout.parseInstance(student).toPrintable());
    }
}

class Student {
    private String name;
    private Integer age;
    private String address;
}

//运行结果:
15:55:51.499 [main] INFO com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //转为二进制后缀为001,表示无锁状态。
  0   8                     (object header: mark)     0x0000001963006a01 (hash: 0x1963006a; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

15:55:51.501 [main] INFO com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //转为二进制后缀为000,表示轻量级锁状态。
                                                      //thin lock:轻量级锁
  0   8                     (object header: mark)     0x000000f8921ff328 (thin lock: 0x000000f8921ff328)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

15:55:51.501 [main] INFO com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      ////转为二进制后缀为001,表示无锁状态。
  0   8                     (object header: mark)     0x0000001963006a01 (hash: 0x1963006a; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

撤销情况二:

当其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁。

@Slf4j
public class Test {

    //需要添加JVM参数:-XX:BiasedLockingStartupDelay=0 来禁用掉偏向锁延迟
    public static void main(String[] args)  {
        Student student = new Student();
        new Thread(() -> {
            log.debug(ClassLayout.parseInstance(student).toPrintable());
            synchronized (student) {
                log.debug(ClassLayout.parseInstance(student).toPrintable());
            }
            log.debug(ClassLayout.parseInstance(student).toPrintable());
            synchronized (Test.class) {
                //唤醒t2线程
                Test.class.notify();
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (Test.class) {
                try {
                    //等待t1线程唤醒
                    Test.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug(ClassLayout.parseInstance(student).toPrintable());
            synchronized (student) {
                log.debug(ClassLayout.parseInstance(student).toPrintable());
            }
            log.debug(ClassLayout.parseInstance(student).toPrintable());
        }, "t2").start();
    }
}

class Student {
    private String name;
    private Integer age;
    private String address;
}

//执行结果:
16:12:07.380 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //偏向锁
  0   8                     (object header: mark)     0x0000000000000005 (biasable; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

16:12:07.383 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //偏向锁
  0   8                     (object header: mark)     0x00000142f8b9e805 (biased: 0x0000000050be2e7a; epoch: 0; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

16:12:07.383 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //偏向锁
  0   8                     (object header: mark)     0x00000142f8b9e805 (biased: 0x0000000050be2e7a; epoch: 0; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

16:12:07.384 [t2] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //偏向锁
  0   8                     (object header: mark)     0x00000142f8b9e805 (biased: 0x0000000050be2e7a; epoch: 0; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

16:12:07.385 [t2] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //轻量级锁
  0   8                     (object header: mark)     0x000000022efff4c0 (thin lock: 0x000000022efff4c0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

16:12:07.385 [t2] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //无锁
  0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

撤销情况三 调用wait/notify

@Slf4j
public class Test {

    //需要添加JVM参数:-XX:BiasedLockingStartupDelay=0来禁用掉偏向锁延迟
    public static void main(String[] args) throws InterruptedException {
        Student student = new Student();
        new Thread(() -> {
            log.debug(ClassLayout.parseInstance(student).toPrintable());
            synchronized (student) {
                log.debug(ClassLayout.parseInstance(student).toPrintable());
                try {
                    student.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug(ClassLayout.parseInstance(student).toPrintable());
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                Thread.sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (student) {
                System.err.println("notify");
                student.notify();
            }
        }, "t2").start();
    }
}

class Student {
    private String name;
    private Integer age;
    private String address;
}

//执行结果:
19:27:59.991 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //可偏向锁状态。
                                                      //biasable:表示可以偏向。
  0   8                     (object header: mark)     0x0000000000000005 (biasable; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

19:27:59.993 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //偏向锁。
                                                      //biased:表示已经偏向。
  0   8                     (object header: mark)     0x0000022b4cb8d805 (biased: 0x000000008ad32e36; epoch: 0; age: 0)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

notify
19:28:04.630 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF  SZ                TYPE DESCRIPTION               VALUE
                                                      //重量级锁
  0   8                     (object header: mark)     0x0000022b2de70e4a (fat lock: 0x0000022b2de70e4a)
  8   4                     (object header: class)    0xf800ef95
 12   4    java.lang.String Student.name              null
 16   4   java.lang.Integer Student.age               null
 20   4    java.lang.String Student.address           null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

批量重偏向

如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID 当撤销偏向锁阈值超过 20 次后,jvm 会这样觉得自己偏向错了,于是会在给这些对象加锁时重新偏向至加锁线程。

@Slf4j
public class Test1 {
    //需要添加JVM参数:-XX:BiasedLockingStartupDelay=0来禁用掉偏向锁延迟
    private static void test3() throws InterruptedException {
        Vector<Dog> list = new Vector<>();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                Dog d = new Dog();
                list.add(d);
                synchronized (d) {
                    log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                }
            }
            synchronized (list) {
                list.notify();
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            synchronized (list) {
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("===============> ");
            for (int i = 0; i < 30; i++) {
                Dog d = list.get(i);
                log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                synchronized (d) {
                    log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                }
                log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
            }
        }, "t2");
        t2.start();
    }
}

//执行结果
[t1] - 0 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 1 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 2 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 3 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 4 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 5 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 6 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 7 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 8 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 9 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 10 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 11 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 12 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 13 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 14 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 15 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 16 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 17 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 18 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11100000 0000010
[t1] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - ===============>
[t2] - 0 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 0 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 1 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 1 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 2 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 2 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 2 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 3 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 3 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 3 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 4 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 4 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 4 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 5 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 5 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 5 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 6 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 6 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 6 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 7 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 7 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 7 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 8 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 8 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 9 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 9 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 9 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 10 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 10 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 10 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 11 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 11 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 11 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 12 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 12 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 12 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 13 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 13 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 13 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 14 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 14 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 14 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 15 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 15 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 15 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 16 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 16 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 16 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 17 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 17 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 17 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 18 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 18 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 18 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101

批量撤销

当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的

@Slf4j
public class Test1 {
    static Thread t1, t2, t3;
    //需要添加JVM参数:-XX:BiasedLockingStartupDelay=0来禁用掉偏向锁延迟
    private static void test4() throws InterruptedException {
        Vector<Dog> list = new Vector<>();
        int loopNumber = 39;
        t1 = new Thread(() -> {
            for (int i = 0; i < loopNumber; i++) {
                Dog d = new Dog();
                list.add(d);
                synchronized (d) {
                    log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                }
            }
            LockSupport.unpark(t2);
        }, "t1").start();
        

        t2 = new Thread(() -> {
            LockSupport.park();
            log.debug("===============> ");
            for (int i = 0; i < loopNumber; i++) {
                Dog d = list.get(i);
                log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                synchronized (d) {
                    log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                }
                log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
            }
            LockSupport.unpark(t3);
        }, "t2").start();


        t3 = new Thread(() -> {
            LockSupport.park();
            log.debug("===============> ");
            for (int i = 0; i < loopNumber; i++) {
                Dog d = list.get(i);
                log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                synchronized (d) {
                    log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
                }
                log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
            }
        }, "t3");
        t3.start();
        t3.join();
        log.debug(ClassLayout.parseInstance(new Dog()).toPrintableSimple(true));
    }
}

锁消除

锁消除是在编译器级别的事情。在即时编译器时,如果发现不可能被共享的对象,则可以消除这 些对象的锁操作,多数是因为程序员编码不规范引起。 Just In Time Compiler,一般翻译为即时编译器。

/**
 * 锁消除
 * 从JIT角度看相当于无视它,synchronized (o)不存在了,这个锁对象并没有被共用扩散到其它线程使
 * 用,极端的说就是根本没有加这个锁对象的底层机器码,消除了锁的使用。
 */
public class LockClearUPDemo {
    static Object objectLock = new Object();//正常的

    public void m1() {
        //锁消除,JIT会无视它,synchronized(对象锁)不存在了。不正常的
        Object o = new Object();

        synchronized (o) {
            System.out.println("-----hello LockClearUPDemo" + "\t" + o.hashCode() + "\t" + objectLock.hashCode());
        }
    }

    public static void main(String[] args) {
        LockClearUPDemo demo = new LockClearUPDemo();
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                demo.m1();
            }, String.valueOf(i)).start();
        }
    }
}

锁粗化

通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完 公共资源后,应该立即释放锁。但是,凡事都有一个度,如果对同一个锁不停的进行请求、同步 和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化。

/**
 * 锁粗化
 * 假如方法中首尾相接,前后相邻的都是同一个锁对象,那JIT编译器就会把这几个synchronized块合并成一个大块,
 * 加粗加大范围,一次申请锁使用即可,避免次次的申请和释放锁,提升了性能
 */
public class LockBigDemo {

    static Object objectLock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (objectLock) {
                System.out.println("11111");
            }
            synchronized (objectLock) {
                System.out.println("22222");
            }
            synchronized (objectLock) {
                System.out.println("33333");
            }
        }, "a").start();

        new Thread(() -> {
            synchronized (objectLock) {
                System.out.println("44444");
            }
            synchronized (objectLock) {
                System.out.println("55555");
            }
            synchronized (objectLock) {
                System.out.println("66666");
            }
        }, "b").start();

    }
}