同步关键字synchronized的实现原理

作者: 特洛伊的人字拖 | 来源:发表于2020-05-05 23:39 被阅读0次

    同步关键字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.png

    JVM把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。

    相关文章

      网友评论

        本文标题:同步关键字synchronized的实现原理

        本文链接:https://www.haomeiwen.com/subject/ftebghtx.html