synchrnoized 和 reentrantlock 的底层实现及重入的底层原理

小助手 面试 42

(1) synchronized的底层实现

synchronized是用来保证线程同步,用的锁存在java对象头中,利用monitorenter和monitorexit指令实现,monitorenter指令是在编译后插入到同步代码块开始位置,而monitorexit是插入到方法结束后和异常处。jdk1.6之后引入了大量的优化,这其中又涉及到锁的四种升级状态:new(无锁) →偏向锁→轻量级锁(自旋锁)→重量级锁。而底层,synchrnoized是利用操作系统的Mutex Lock(互斥锁)来实现的,

(2)synchronized同步块对同一条线程来说是可以重入的,不会出现自己把自己锁死的问题

同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。由于java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一条线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间,所以synchronized是java语言中一个重量级操作。

(3)重入锁实现可重入性原理或机制

每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记录下锁的持有线程,并且将计数器置为1;此时其他线程请求该锁,则必须等待;而持有该锁的线程如果再次请求这个锁,就可以再次拿到这把锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器变为0,则释放锁。

(4)reentantlock的底层实现原理

ReentrantLock是基于AQS的,AQS是Java并发包中众多同步组件的构建基础,它通过一个int类型的状态变量state和一个FIFO队列来完成共享资源的获取,线程的排队等待等。AQS是个底层框架,采用模板方法模式,它定义了通用的较为复杂的逻辑骨架,比如线程的排队,阻塞,唤醒等,将这些复杂但实质通用的部分抽取出来,这些都是需要构建同步组件的使用者无需关心的,使用者仅需重写一些简单的指定的方法即可(其实就是对于共享变量state的一些简单的获取释放的操作)。ReentrantLock的处理逻辑,其内部定义了三个重要的静态内部类,Sync,NonFairSync,FairSync。Sync作为ReentrantLock中公用的同步组件,继承了AQS(要利用AQS复杂的顶层逻辑嘛,线程排队,阻塞,唤醒等等);NonFairSync和FairSync则都继承Sync,调用Sync的公用逻辑,然后再在各自内部完成自己特定的逻辑(公平或非公平)。

(5)reentantlock的非公平锁的实现

对于共享变量state,先获取state值,若为0,意味着此时没有线程获取到资源,CAS将其设置为1,设置成功则代表获取到排他锁了;若state大于0,肯定有其他线程已经抢占资源,此时再去判断是否就是自己抢占的,是的话,state累加,返回true,重入成功,state的值即是线程重入的次数,其他情况,则获取苏失败;

(6)reentantlock的可重入公平锁的实现

公平锁的大致逻辑与非公平锁是一致的,不同的地方在于有了!hasQueuedPredecessors()这个判断逻辑,即便state为0,也不能贸然直接去获取,要先去看有没有还在排队的线程,若没有,才能尝试去获取,做后面的处理。反之,返回false,获取失败。

不知道这样解释是否完备,还请大家指正。

回复

我来回复
  • 暂无回复内容