编程语言
首页 > 编程语言> > java中的synchronized只是重量级锁吗?聊一聊synchronized锁升级流程

java中的synchronized只是重量级锁吗?聊一聊synchronized锁升级流程

作者:互联网

synchronized这个关键字,原来的印象就是一个重量级锁,也就是悲观锁,直接锁住代码段,剩余的线程进入到阻塞队列中,效率极低,实际上呢,在jdk1.6之后,synchronized的内部进行了优化,它不再是一个简单的重量级锁,它为了试用所有的情况,有了一个锁升级流程:无锁 -》 偏向锁  -》 轻量级锁 -》 重量级锁,接下来我们仔细的聊一下所谓的锁升级流程。

 

首先,现来看一下,synchronized的使用方法:

1、对一个对象进行加锁

synchronized(this){
    //代码
}

2、对一个方法进行加锁

public synchornized void test(){
    //代码
}

实际上,无论是对一个对象进行加锁还是对一个方法进行加锁,实际上,都是对对象进行加锁。也就是说,对于方式2,实际上虚拟机会根据synchronized修饰的是实例方法还是类方法,去取对应的实例对象或者Class对象来进行加锁。

所以既然是对对象加锁,我们是不是应该了解下对象的结构呢,楼主的上一篇文章有关于对象结构的,不再多说。锁的信息都是存在对象头中的MarkWord中的。结构如下:

 

 一、无锁状态

当一个对象被创建出来时,为无锁状态,所标记位位01,是否偏向锁位0。

 

二、偏向锁

当一个线程执行到锁相关的代码段时,就会将是否偏向锁置为1,此时MarkWord结构如下:

bit fields 是否偏向锁锁标志位
threadId epoch 1 01

 

 

 

此时有线程占据了这个锁,这也就是偏向锁。

这也就到了偏向锁,这个锁会偏向于第一个获得它的线程,在接下来的执行过程中,假如该锁没有被其他线程所获取,没有其他线程来竞争该锁,那么持有偏向锁的线程将永远不需要进行同步操作。

假如,这时候来了一个线程,也来抢占该锁,处理流程如下:

1、判断线程id是否和MarkWord中的线程id相同,如果相同,那么直接执行代码块,否则执行下一步。

2、查看对象是否为可偏向,如果为0,那么进行CAS操做,将MarkWord中的否偏向锁置为1,并记录id。如果不是,执行下一步骤。

3、这时候就出现了竟争锁的情况,新线程,会尝试CAS操作,来更新线程id,如果失败,就进行锁的撤销或升级为轻量级锁。

 

如果失败,就去撤销锁,首先需要判断,MarkWord中存放的线程id是否还存活,如果已经死亡,就撤销锁,然后新线程,获取锁,否则升级为轻量级锁。

 

其中简单聊一下撤销锁:

锁的对象头中偏向着线程1,因为它不知道线程1什么时候来,所以一直偏向着,就算线程1已经死亡了。所以撤销锁的时候,先检查对象头所指向的线程是否存活,如果不存活,那么偏向锁撤销为无锁,线程2就拿到了锁,如果存在,那么线程1目前没有拿着锁而在干别的事情,这样锁就在不同时间段被不同线程访问了升级为轻量级锁。

 

所以,如果程序肯定是两个线程竟争,我们可以一开始就把偏向锁这个默认功能给关闭,否则浪费大量的资源。

 

三、轻量级锁

简单描述下偏向锁升级为轻量级锁的过程:

1、线程在自己的线程栈中,新建一个锁记录LockRecord。

2、将LockRecord中的对象指针,指向锁对象,然后将锁对象的MarkWord复制到LockRecord中。

3、将锁对象的MarkWord替换为为指向LockRecord的指针。

 

然后MarkWord更新为:

bit fields锁标志位
指向LockRecord的指针 00

 

 

 

然后简单聊聊轻量级锁。

轻量级锁又叫自旋锁。

自旋锁也就是当一个线程占据着锁时,这时候另一个线程来了,发现锁被占用,就开始进行不停的尝试CAS操作,也就是不停的执行for循环,来不停的尝试获取该锁,知道获取锁之后,结束。

当线程太多之后,就会出现一个问题,假如有100个线程竟争资源,有99个在不停的执行for循环,这个cpu的消耗是非常可怕的。所以,线程太多了,就需要让线程阻塞,然后执行了,默认情况下,自旋的次数为10次,用户可以通过-XX:PreBlockSpin来进行更改,超过了,就升级为重量级锁。

 

四、重量级锁

升级为重量级锁之后,MarkWord中发生变化,如下:

bit fields锁标志位
指向Mutex的指针 10

 

 

 

重量级锁是依赖对象内部的monitor锁来实现的,而monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被成为互斥锁。

重量级锁的工作流程如下:当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cup。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。所以重量级锁的开销还是很大的。

 

这就是synchronized中的锁升级流程。

标签:MarkWord,加锁,java,synchronized,对象,聊一聊,线程,重量级,偏向
来源: https://www.cnblogs.com/mcjhcnblogs/p/14226505.html