浅谈Synchronized关键字

浅谈Synchronized

  1. synchronized简介

synchronized可以说是并发编程的元老级角色,大部分时候被称为重量级锁,但是在1.6之后的为了减弱这种重量级的影响,对其进行了各种优化,像是在1.6的优化中引入了偏向锁和轻量级锁以及锁的消除和锁的升级

  1. synchronized锁的范围

可以这样说,java中任何一个对象都可以作为锁,具体可以表现为下面三种形式
对于普通的同步方法,锁的是当前实例对象
对于静态同步方法,锁的是当前类的CLASS对象
对于同步的方法块,锁的是括号内的对象

  1. synchronized的实现原理

从JVM操作规范中,我们可以知道,JVM几区进入和退出Monitor对象来实现方法和代码块的同步
这里要知道,两者的细节是不同的
代码块的同步使用的monitorenter和monitorexit指令实现
方法使用了另一种来实现的,具体并没有详细说明,但是方法的同步同样可以使用这两条指令来实现
原理就是monitorenter在编译的时候添加到同步代码块开始的位置,monitorexit插入到同步代码块结束和异常的位置,JVM保证每个monitorenter都有与之对应的monitorexit指令,每个对象都有一个monitor对象,当他的monitor被持有之后,就会被认为是被占有也就是加锁了,当线程执行到这个指令的时候,就回去尝试获取monitor,也就是去请求获取锁
浅谈Synchronized关键字

  1. Java对象头

synchronized的锁存放于对象的对象头中,对象是数组类型,会用三个字宽,非数组类型占用两个字宽
浅谈Synchronized关键字
而Mark Word详细内容如下
浅谈Synchronized关键字
因为标记位的不同,可能会存储不同的数据,详细如下
这是32位虚拟机的情况浅谈Synchronized关键字
64位虚拟机的情况又略有不同
浅谈Synchronized关键字

  1. 锁的状态

JDK1.6的时候为了减少获得锁和释放锁带来的内存消耗,引入了偏向锁和轻量级锁的概念,这样锁一共有了四种状态:偏向锁状态,无锁状态,轻量级锁状态,重量级锁状态,锁可以升级不能降级
偏向锁
在研究中发现,大多数情况锁不存在多线程竞争,总是由同一个线程获得锁,这样就引入了偏向锁的概念,具体实现就是当一个线程访问同步代码块获取锁的时候,会使用CAS将线程ID设置到对象的对象头,之后只要是这个线程是自己的就表示没有竞争,所以就不需要进行CAS操作,当获取锁失败,就会查看对象头标识是否设置了偏向锁,如果没有就CAS竞争锁,如果设置了,就尝试使用CAS将对象头中的偏向锁指向该线程
浅谈Synchronized关键字
这里要重点注意,偏向锁是等到有竞争的时候才会释放锁的机制
这里就出现了锁撤销的问题,撤销的时候首先会暂停拥有偏向锁的线程,检查持有偏向锁的线程是否还活着,如果不处于活动状态他会将他设置成无锁,如果仍然活着,那么就会出现两种情况
一种是栈中的锁记录重新偏向其他线程
一种是恢复到无锁,或者进行锁升级
浅谈Synchronized关键字
这里可以看的出只有在偏向锁不是本线程ID并且CAS失败的时候,才会升级为轻量级锁
轻量级锁
这里可以从轻量级锁加锁和解锁开始谈起
在执行同步代码块的时候,首先会在栈帧中创建存储锁记录的空间,空间存储对象的MarkWord,之后使用CAS来将对象头中的MarkWord替换为锁记录的指针,成功的话就代表获取了锁,失败的话就自旋的获取锁,如果自选获取锁失败,那么就会升级为重量级锁
浅谈Synchronized关键字
浅谈Synchronized关键字
浅谈Synchronized关键字

这里CAS失败会出现两种情况,一种是已经升级为重量级锁
另一种是锁重入
解锁的时候如果锁记录的值不是null,那么就是使用CAS进行恢复,成功的话就会解锁成功
失败的话说明轻量级锁膨胀为重量级锁,进入重量级锁解锁过程
解锁升级为重量级锁的过程
浅谈Synchronized关键字
首先我的Thread-0已经获取了锁,锁记录已经和markword进行交换,这是Thread-1来获取锁,CAS失败,这是就会升级为重量级锁,将对象指向Monitor,并且将Thread-1放进Monitor锁对象的EntryList的队列里,此时Monitor的owner指向Thread-0
浅谈Synchronized关键字
这是Thread-0解锁,进行CAS将锁记录和MarkWord进行交换,这时因为已经升级成了重量级锁,对象中存储的已经不是锁记录,所以CAS失败,就会进入重量级锁的解锁流程,也就是将owner置为null,并且唤醒EntryList中阻塞的线程,进行锁的竞争
浅谈Synchronized关键字
这里是一个重量级锁的解锁流程

上述问题说完之后,引申出来三个新的概念,自旋锁,批量重偏向,批量撤销
自旋锁
就是当一个线程获取锁之后,另一个线程会循环等待尝试获取锁
有点就是在于线程会一直保持用户态,当线程没有获取锁阻塞住的时候,会进入内核态,下次尝试获取锁的时候会再切换回用户态,这样就产生了消耗
批量重偏向
如果对象被多个线程访问没有竞争,这时偏向t1的仍有机会偏向t2,重偏向会终止对象线程id,
当撤销偏向锁超过20次,jvm会认为偏向错误,于是会在加锁时进行重新偏向
批量撤销
当撤销超过40次,就会认为不该进行偏向,从而把对象都变成不可偏向的

浅谈Synchronized关键字

上一篇:docker进入MySQL容器,在线面试指南


下一篇:web前端开发培训公司,css高度