Synchronized偏向锁批量重偏向与批量撤销

1.批量重偏向

  • intx BiasedLockingBulkRebiasThreshold = 20 默认偏向锁批量重偏向阈值

  • 可以通过JVM参数 -XX:BiasedLockingBulkRebiasThreshold手动设置阈值

    起因:

    当一个线程A对某个类生成大量的对象的对象,这些对象偏向了A,后面突然有来了个线程B去抢线程A生成的对象,就会导致偏向锁的升级为轻量级锁,如果总是在不断的升级锁,到了一定的阈值,JVM 就会对类的所有对象进行批量的重新偏向与B。

    测试代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    public class BiasedLock {
    public static void main(String[] args) throws InterruptedException {
    //偏向延时
    Thread.sleep(5000);
    final ArrayList<Dog> dogArrayList = new ArrayList<>();
    Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
    while (dogArrayList.size() < 60) {
    Dog dog = new Dog();
    synchronized (dog){
    dogArrayList.add(dog);
    HeaderPrint.parseHeaderInfo(dog);
    }
    }
    //保持线程不要退出
    try {
    Thread.sleep(100000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    }
    };

    Runnable runnable2 = new Runnable() {
    @Override
    public void run() {
    for (int i = 0; i < 50; i++) {
    Dog dog = dogArrayList.get(i);
    synchronized (dog){
    System.out.print((i+1)+":");
    dogArrayList.add(dog);
    HeaderPrint.parseHeaderInfo(dog);
    }
    }
    //保持线程不要退出
    try {
    Thread.sleep(100000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    };
    new Thread(runnable1).start();
    //保证线程1生成完毕
    Thread.sleep(5000);
    new Thread(runnable2).start();
    }
    }
  • 我们可以看到,线程2启动的时候,是从偏向锁升级到了 轻量级锁

  • 一直升级到 阈值20的时候,触发JVM优化机制,JVM认为偏向的不对,怎么总是线程2来强=抢,总的升级锁,比较麻烦,所以JVM直接让剩下dog对象,直接重新偏向到线程2

  • 线程0 的偏向值 thread:54:000000000000000001111111101001001000101100000011000100(HEX:1fe922c0c4)

  • 在 第 20个是的时候,就已经重新偏向了

  • Xnip2020-05-20_00-54-05.png

  • 后面的也都是偏向锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    50:
    --------START-----------Thread-2
    --------偏向锁--------
    thread:54:000000000000000001111111101001001000101100011000001110(HEX:1fe922c60e)
    epoch:2:01
    unused:1:0
    age:4:0000
    biased_lock:1:1
    --------END-----------Thread-2

    2.批量撤销

  • intx BiasedLockingBulkRevokeThreshold = 40 默认偏向锁批量撤销阈值

  • 可以通过JVM参数 -XX:BiasedLockingBulkRevokeThreshold手动设置阈值

    起因:

    在多个线程竞争剧烈的情况下,使用偏向锁将会降低效率,于是乎产生了批量撤销机制。

    测试代码:

  • 批量重偏向和批量撤销是针对类的优化。

  • 偏向锁只会重偏向一次,后面就是升级锁

  • 当某个类已经触发批量撤销机制后,该类的对象不会再使用偏向锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    public static void main(String[] args) throws InterruptedException {
    final CountDownLatch countDownLatch = new CountDownLatch(2);
    //偏向延时
    Thread.sleep(5000);
    final ArrayList<Dog> dogArrayList = new ArrayList<>();
    Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
    while (dogArrayList.size() < 80) {
    Dog dog = new Dog();
    synchronized (dog) {
    dogArrayList.add(dog);
    }
    }

    //保持线程不要退出
    try {
    Thread.sleep(50000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    };

    Runnable runnable2 = new Runnable() {
    @Override
    public void run() {
    for (int i = 0; i <39; i++) {
    if (i == 38) {
    System.out.println((i + 1) + ":");

    //这里主要是给主线程留出时间,让主线程打印出一个new Dog
    countDownLatch.countDown();
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    }
    Dog dog = dogArrayList.get(i);
    synchronized (dog) {
    dogArrayList.add(dog);
    try {
    Thread.sleep(20);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }

    //保持线程不要退出
    try {
    Thread.sleep(50000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    };


    new Thread(runnable1).start();
    //保证线程1生成完毕
    Thread.sleep(3000);

    //重新偏向线程2
    new Thread(runnable2).start();
    Thread.sleep(3000);

    //线程3导致偏向锁升级
    new Thread(runnable2).start();

    //线程3导致锁升级到39的时候,new出来的对象,默认是可偏向的状态
    countDownLatch.await();
    HeaderPrint.parseHeaderInfo(new Dog());

    //保证之后
    Thread.sleep(3000);
    //第39之后的新对象,已经没有偏向了,偏向被撤销了
    HeaderPrint.parseHeaderInfo(new Dog());

    }
  • 输出

  • 第39的时候,默认输出的是,可偏向状态

  • 第39之后,new 的对象,已经被取消默认可偏向的状态了,成无锁了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    39:
    --------START-----------main
    --------偏向锁--------
    thread:54:000000000000000000000000000000000000000000000000000000(HEX:0)
    epoch:2:01
    unused:1:0
    age:4:0000
    biased_lock:1:1
    --------END-----------main


    --------START-----------main
    --------无锁-----------
    unused:25:0000000000000000000000000
    identity_hashcode:31: 0000000000000000000000000000000 (Hex:0)
    unused:1:0
    age:4:0000
    biased_lock:1:0
    lock:2:01
    --------END-----------main