synchronized在jdk1.6之前,一直都是重量级锁。为什么称之为重量级锁呢,主要是因为锁的资源是通过操作系统去申请 ,所以比较重量级。

在jdk1.6之后,jvm对synchronized进行了升级,对锁的粒度进行了细化,分为无锁,偏向锁,轻量级锁,重量级锁。性能得到了很大的提升。

锁升级的过程是怎样的呢?

主要一开始是无锁的状态,如果偏向锁未启动,当有其他线程竞争时,会升级为轻量级锁,如果偏向锁启动,则会升级为偏向锁,如果竞争激烈则会从偏向锁升级为重量级锁,如果不激烈则有偏向锁升级为轻量级锁,再由轻量级锁升级为重量级锁。

过程主要如下,参考马士兵老师的锁升级过程

yield java锁升级 java锁升级细节_数据

 

接下来我们就要验证下锁升级的过程。在验证锁升级的过程之前,先了解下java对象的结构,因为锁的信息就是放在对象信息的markword中。

锁状态

25位

31位

1位

4bit

1bit

偏向锁位

2bit

锁标志

无锁

unused

hashcode(如果有调用)

unused

分代年龄

0

0

1

 

 

 

 

 

 

 

 

锁状态

54位

2位

1位

4bit

1bit

偏向锁位

2bit

锁标志

偏向锁

当前线程指针

Epoch

unused

分代年龄

1

0

1

 

 

 

 

 

 

 

 

锁状态

62位

2bit

锁标志

轻量级锁

指向线程栈中Lock Record的指针

0

0

重量级锁

指向互斥量(重量级锁)的指针

1

0

GC标记信息

CMS过程用到的标记信息

1

1

根据markword的结构可以看到低位的2位分别表示锁的信息,第三位表示偏向锁的信息。

接下来我们来验证下锁升级的过程,这里需要借助下JOL这个工具,打印出对象的信息。在maven中添加依赖。

<dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>

现在来验证下锁升级过程。

先创建一个对象和方法,然后再多线程调用MyObject这个对象信息,再打印出对象信息,看看锁升级的过程。

public class MyObject {
    public synchronized void test(){
        System.out.println("test");
    }
}



import org.openjdk.jol.info.ClassLayout;

import java.util.ArrayList;
import java.util.List;

public class TestSynchronizedUpgrade {

    public static void main(String[] args) throws InterruptedException {
        MyObject o = new MyObject();
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            Thread thread = new Thread(() -> o.test());
            threads.add(thread);
        }

        for (Thread thread : threads) {
            thread.start();
            // ClassLayout输出对象信息
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }


    }
}

 

运行输出为:

test.jol.MyObject 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)                           92 c3 00 20 (10010010 11000011 00000000 00100000) (536920978)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

test.jol.MyObject 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)                           92 c3 00 20 (10010010 11000011 00000000 00100000) (536920978)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

test.jol.MyObject 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)                           92 c3 00 20 (10010010 11000011 00000000 00100000) (536920978)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

test.jol.MyObject 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)                           92 c3 00 20 (10010010 11000011 00000000 00100000) (536920978)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

test
test
test
test
test.jol.MyObject object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           88 ef 3c 1c (10001000 11101111 00111100 00011100) (473755528)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           92 c3 00 20 (10010010 11000011 00000000 00100000) (536920978)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

test
test.jol.MyObject object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           8a 36 88 18 (10001010 00110110 10001000 00011000) (411580042)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           92 c3 00 20 (10010010 11000011 00000000 00100000) (536920978)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

我们先挑出来一个看,看到第三行的数据,前四个字节的值为01 00 00 00 这是16进制数据后面括号中 (00000001 00000000 00000000 00000000)表示的二进制数据,也就是bit的数据,也就是低位三个数据是001,对照上面markword的值可以知道,开始的时候是无锁的状态。在运行到一段时间之后,低位会变成00也就是轻量级锁,再后面会变成10,也就是升级成了重量级锁。

那为什么没有偏向锁呢?查了资料发现其实是jdk在开始时偏向锁是不启动的,可能是在开始运行是并没有什么锁的竞争,所以偏向锁没有启动,当运行一段时间之后才会启动,大概是4秒之后才会启动偏向锁。

但是我在测试偏向锁的时候,发现会偏向锁变成无锁,再变成轻量级锁,再变成无锁,这点有点不太能理解,如果有人明白告知下,不胜感激!