线程学习(12)-锁膨胀

锁膨胀其实就是锁升级的过程,从偏离锁升级为轻量级锁,以及从轻量级锁升级成重量级锁的过程。这里以从轻量级锁升级成重量级锁的过程直接上代码吧。

轻量级锁升级成重量级锁的过程过程。

package com.bo.threadstudy.four;

import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;

import java.util.concurrent.locks.LockSupport;

/**
 * 锁膨胀模拟
 */
@Slf4j
public class LockSwellTest {
    private static Object lock = new Object();

    private static volatile boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        log.debug("初始状态锁状态:");
        getStatus();

        Thread t1;
        Thread t2;

        t2 = new Thread(() -> {
            LockSupport.park();

            synchronized (lock) {
                //t2竞争锁的状态
                log.debug("t2竞争锁的状态为");
                getStatus();
            }

        }, "t2");
        t2.start();


        t1 = new Thread(() -> {
            synchronized (lock) {
                log.debug("刚进入synchronized方法锁状态:");
                //刚进入,肯定是轻量级锁
                getStatus();

                //使用park方法来解决,保证之前的代码在t2的synchronized前解决
                LockSupport.unpark(t2);
                
                //开始竞争了
                log.debug("t1竞争时锁状态:");
                getStatus();
            }

        }, "t1");
        t1.start();


        //最终锁状态为
        Thread.sleep(2000);
        log.debug("最终锁状态为:");
        getStatus();
    }

    private static void getStatus() {
        String s = ClassLayout.parseInstance(lock).toPrintable();
        log.debug(s);
    }

}

结果如下:

18:08:44.755 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - 初始状态锁状态:
18:08:49.985 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      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

18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - 刚进入synchronized方法锁状态:
18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           e0 f1 2f bb (11100000 11110001 00101111 10111011) (-1154485792)
      4     4        (object header)                           16 00 00 00 (00010110 00000000 00000000 00000000) (22)
      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

18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - t1竞争时锁状态:
18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           1a 61 d1 fe (00011010 01100001 11010001 11111110) (-19832550)
      4     4        (object header)                           c1 01 00 00 (11000001 00000001 00000000 00000000) (449)
      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

18:08:49.985 [t2] DEBUG com.bo.threadstudy.four.LockSwellTest - t2竞争锁的状态为
18:08:49.985 [t2] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           1a 61 d1 fe (00011010 01100001 11010001 11111110) (-19832550)
      4     4        (object header)                           c1 01 00 00 (11000001 00000001 00000000 00000000) (449)
      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

18:08:52.005 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - 最终锁状态为:
18:08:52.005 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      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

Disconnected from the target VM, address: '127.0.0.1:62238', transport: 'socket'

Process finished with exit code 0

看OFFSET  SIZE   TYPE DESCRIPTION                               VALUE这行下面第一行的括号内部内容的前八位,可以看到,初始状态锁状态为001(无锁),刚进入synchronized时为000(轻量级锁),在使用Locksupport.unpark()方法来解除暂停时,发生竞争现象。此时锁状态是010(重量级锁),最终从synchronized中出来后是001(无锁)。

锁膨胀的流程

1.当t1使用object来施加轻量级锁时,t0已经对Object对象施加了轻量级锁了并正在执行Synchronized中的代码。

2.此时t1试图采用CAS以Lock Record锁记录来替换Object中mark word中的值,发现内部的值已经是t0的锁记录地址以及锁状态00(轻量级锁),不能替换。进入锁膨胀流程:

为Object对象申请monitor锁,然后将monitor对象地址存放至object中的mark word中。

然后自身进入monitor对象中的entryList中。

3.在t0执行完成后,使用CAS替换掉mark word中的锁记录信息以及锁状态,发生错误。此时会采用重量级锁的方式,从Object中的mark word查找到monitor对象的地址,设置owner为null,并唤醒entryList中的Blocked线程,来获取到当前锁。

 

这里说了Blocked线程,那么waited线程是否会进入锁膨胀呢?写一下吧。

//测试一下wait线程锁的状态
@Slf4j
class WaitLockTest{

    private static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
           //没有其它线程竞争的情况下,有其它线程竞争肯定是重量级,不用看
           synchronized (lock){
               try {
                   lock.wait();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        }).start();
        Thread.sleep(10000);

        getStatus();
    }


    private static void getStatus() {
        String s = ClassLayout.parseInstance(lock).toPrintable();
        log.debug(s);
    }

}

 

单独一个线程,没有发生竞争的情况下,是重量级锁。

不过也正常,阻塞状态,用到了monitor中的waitSet,肯定是重量级了。所以,三种阻塞状态的数据,必定是重量级锁。

 


 


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