51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

有关synchronized锁的知识点,我用一篇文章总结了

关于Java多线程锁的升级原理,这篇文章会让你另有收获

# 2.1 Java对象内存布局 {#_2-1-java对象内存布局}

在了解锁升级原理之前我们首先要了解一下Java对象在内存中的布局

对象头用于存储对象的元数据信息,包括运行时数据和类型指针、实例数据存储的是真正有效数据、对齐填充主要补充字节,使得内存所占字节能被8整除。

其中对象头Header占12个字节:Mark Word占8个字节,类型指针class pointer占4个字节(默认经过了压缩,如果不开启压缩占8个字节)

实例对象按实际存储有不同大小,对象为空时等于0。

Padding表示对齐,当此时内存所占字节不能被8整除时补上相应字节数。

# 2.2 synchronized锁升级 {#_2-2-synchronized锁升级}

Java中锁升级的最佳实例就是synchronized,之所以要讲上面的内存布局,是因为synchronized把锁信息存放在对象头的MarkWord中。

在早期的jdk版本中,synchronized是一个重量级锁,保证线程的安全但是效率很低。后来对synchronized进行了优化,有了一个锁升级的过程

无锁态(new)-->偏向锁-->轻量级锁(自旋锁)-->重量级锁

通过MarkWord中的8个字节也就是64位来记录锁信息。

锁升级过程详解: 当给一个对象增加synchronized锁之后,相当于上了一个偏向锁

当有一个线程去请求时,就把这个对象MarkWord的ID改为当前线程指针ID(JavaThread),只允许这一个线程去请求对象。

当有其他线程也去请求时,就把锁升级为轻量级锁。每个线程在自己的线程栈中生成LockRecord,用CAS自旋操作将请求对象MarkWordID改为自己的LockRecord,成功的线程请求到了该对象,未成功的对象继续自旋。

如果竞争加剧,当有线程自旋超过一定次数时(在JDK1.6之后,这个自旋次数由JVM自己控制),就将轻量级锁升级为重量级锁,线程挂起,进入等待队列,等待操作系统的调度。

# 2.3 锁消除 {#_2-3-锁消除}

说了锁升级过程,有必要说一下说一下锁消除和锁粗化。

在某些情况下,如果JVM认为不需要锁,会自动消除锁,比如下面这段代码:

public void add(String a,String b){
    StringBuffer sb=new StringBuffer();
    sb.append(a).append(b);
}

StringBuffer是线程安全的,但是在这个add方法中stringbuffer是不能共享的资源,因此加锁只会徒增性能消耗,JVM就会消除StringBuffer内部的锁。

# 2.4 锁粗化 {#_2-4-锁粗化}

在某些情况下,JVM检测到一连串的操作都在对同一个对象不断加锁,就会将这个锁加到这一连串操作的外部,比如:

StringBuffer sb=new StringBuffer();
while(i<100){
    sb.append(str);
    i++;
}

上述操作StringBuffer每次添加数据都要加锁和解锁,连续100次,这时候JVM就会将锁加到更外层(while)部分。

赞(4)
未经允许不得转载:工具盒子 » 有关synchronized锁的知识点,我用一篇文章总结了