Sychronized锁升级了解?无锁、偏向锁、轻量级锁、重量级锁
一、对synchronized的基本认识
synchronized同步锁是一个JVM层面的锁,之所以说他是一个jvm层面的锁,是因为开发者不需要关心这个锁是何时获取何时释放,只需要在需要同步的地方添加synchronized即可。
使用synchronized的场景就是在多线程的情况下,多个线程对资源的抢占会出现资源数错乱的现象。
synchronized同步机制可以用来修饰一个静态方法、普通方法、代码块。但是归根结底就属于两类锁对象:对象锁和类对象锁。
修饰不同的方法对应的锁对象也是不一样的。
-
静态方法:对于静态方法来说,锁对象是Class类对象。
-
普通方法:对于普通方法来说,锁对象是this对象。
-
代码块:对于代码块来说,锁对象是任何一个应用对象,可以自己创建。例如:String等等。
synchronized锁还是一个重量级锁,值得说的是在JDK6及其以前,synchronized所是一个重量级锁,使用它的时候对于性能的影响是很大的,不推荐使用;但是在jdk6之后,就对这个synchronized同步锁实现了优化,引入了:无锁、偏向锁、轻量级锁、重量级锁,称为锁升级。对于锁升级的内容在第二个模块做解释。
synchronized同步锁有四个特点:
-
原子性
所谓的原子性,指的就是一次或多次操作中,要么都执行成功(指的是在执行过程中不能受任何外在因素的印象),要么就是不执行。
在Java中,对于基本数据类型的读写是原子性的,比如:i++ i-- ,这些计算其实是分为三步的:1、获得数据 2、计算数据 3、进行赋值 虽然为三步但是是原子操作,用synchronized关键字修饰的方法或代码块就是能够实现原子性,在执行同步代码前,会先获得锁,然后执行,这个时候就没有任何的线程对其进行影响,当执行完毕后,释放锁,即保证了原子性。
-
可见性
所谓的可见性,指类的就是共享资源的改变对于所有的线程都是可见的,都是暴露的。
synchronized和volatile关键字都是可见性的,只不过,synchronized是通过加锁实现的,而volatile是通过背后的缓存一致性协议和嗅探机制实现的。
synchronized通过加锁在多线程环境中可实现锁的可见,其他线程没有获得锁对象就只能进行阻塞,在获得锁对象之前会将线程的工作内存清除掉,从主内存中获得新的值,放在工作内存中,计算完毕之后,会在释放锁之前,将资源刷新会主内存,然后释放锁资源。
-
有序性
所谓的有序性,指的就是会根据代码的顺序有序的执行。
synchronized通过加锁实现了在同一时刻只能有一个线程获得锁资源,对于同步的代码是顺序进行访问的,保证了其有序性。
-
可重入性
所谓的可重入性,指的是已经获得锁资源的线程在此访问相同锁资源的代码时,是可以再次获得该锁资源的,
比如:有A、B、C三个方法,都是相同的锁资源,当执行A方法的时候获得了锁,在A方法中引用了B方法,在B方法中引用了C方法,是可以实现三个方法的一次执行的。
二、synchronized锁的实现
synchronized锁的实现在字节码层面是分不同的情况的。
-
如果当前是对同步代码块实现加锁的话,在字节码文件中,同步代码前后会有monitorenter和monitorexit指令。
为什么会存在两个monitorexit,是为了避免异常出现,而不能及时的释放所得资源。
-
如果当前是对方法实现加锁的话,在字节码文件中,并没有monitorenter和monitorexit,而是会有一个flag标志:ACC_Synchronized标志。
三、synchronized底层实现
说这个synchronized底层实现前,需要说一下对象的内存模型,对象是分为三个部分的:
-
对象头
-
实例数据
-
填充对齐
其中,对象头与锁的关系很密切,不管是synchronized申请锁还是释放锁都是与对象头有关系。
对象头主要的结构由:Mark Word、Class Point
MarkWork:主要存储的就是锁信息、分代年龄、GC标志等等
Class Point:是一个类型执行,JVM可以通过这个指针确定这个对象属于哪个类的实例对象。
synchronized锁在jdk6之后是有四种状态的:无锁、偏向锁、轻量级锁、重量级锁。锁的状态标志,都是存储在MarkWord中的。在synchronized申请锁或者锁升级的时候都是需要对markword进行锁标志获取的。
四、了解synchronized的锁升级过程
synchronized锁的升级是一个不可逆的过程,也就是说,如果从偏向锁最终升级为重量级锁,则就算之后的QPS(每一秒访问量)平稳,也是不能进行锁的状态回溯的。
-
偏向锁
核心思想是:当一个线程获得到了锁,那么对象头的markword的结构就会改变为偏向锁结构。当再次获得锁时,不需要做任何的同步操作,只需要检查markword中的锁标记是不是偏向锁标记,以及当前的线程ID是不是等于markword的ThreadID即可。节省大量申请锁的操作。
-
轻量级锁
轻量级锁是由偏向锁升级而来的,当存在第二个线程申请同一个资源的时候,就会把偏向锁升级为轻量级锁。
-
重量级锁
重量级锁是由轻量级锁升级而来的,指的是在同一时间有多个线程对同一个资源进行竞争,这个时候所得状态就会变成重量级锁。重量级锁的开销也是很大的。