同步关键字synchronized的实现原理
同步关键字synchronized的实现原理
JAVA中锁的概念
自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取。
乐观锁:假设没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,重试修改。
悲观锁:假定会发生并发冲突,同步所有对数据的相关操作,从读取数据就加锁。
独享锁(写):给资源加上写锁,线程可以修改资源,其他线程不能再加锁。
共享锁(读):给资源加上读锁后只能读不能改,其他线程也只能加读锁,不能加写锁。
可重入锁、不可重入锁:线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码。
公平锁、非公平锁:争抢锁的顺序,如果是按先来后到,则为公平。
synchronized同步关键字的特性
synchronized是jvm提供的可重入、独享、悲观、非公平锁
锁优化:锁消除(开启锁消除的参数:-xx:+DoEscapeAnalysis -xx:+EliminateLocks)
锁粗化 JDK做了锁粗化的优化,但我们自己可从代码层面优化
Note:synchronized关键字,不仅实现同步,JMM中规定,synchronized要保证可见性
JAVA对象的头部结构 image-1.png
如果jvm是32位markward就是32位,jvm是64位markword就是64位。加锁的状态存放在对象头的markword中,markword的不同状态就表示对象有没有加锁、加的是什么锁。对象监视器的_count表示重入了多少次,_owner用来记录当前哪个线程获得锁,_EntryList是个等待队列,_waiters是等待池。
synchronized的实现原理
未锁定——>轻量级锁
image-2.png线程01和线程02通过CAS来抢对象的锁,线程01先线程02把Lock record address写进了markword,即获得了轻量级锁。
image-3.png
这时候线程01的owner指向对象的markword,表示这个线程当前获得了此对象的锁。线程02继续做CAS操作就会失败,因为对象头的这段内存已经改变了。失败就会自旋,不停的自旋就会消耗CPU资源,所以自旋达到一定次数就会锁升级。如果此时有第三个线程过来抢锁就直接锁升级,不管线程02的自旋线程次数是否到达锁升级的阀值。
锁升级
image-4.pngJVM把markword的地址修改成对象监视器的地址,锁升级后无论是自旋中的线程02还是刚进来抢锁的线程03都会进入对象监视器的entrylist这个等待队列,线程进入blocked的状态,这样就有效的解决了锁自旋消耗CPU资源的问题。
image-5.png
线程t01调用wait方法挂起线程并且释放锁,对象监视器owner此时为null,线程t01进入等待池,线程t02出队列拿到锁,方法执行完后退。线程t03重复线程t02的步骤。waitset里面的线程状态是waiting,entrylist里面的线程状态是blocked。当t01被当前的owner线程用notify唤醒后,t01出等待池去抢锁,此时owner不为null,线程t01进入等待队列,t01的线程状态也由waiting变成blocked。