JDK成长21:ReentrantLock(四)什么是公平锁、非公平锁、可重入锁?
时间:2023-04-02 00:19:12
Java
经过前面三节,相信大家对ReentrantLock底层的AQS原理有了清晰的认识。接下来给大家介绍一下ReentrantLock中的几个概念:公平锁和非公平锁的概念。ReentrantLock是如何做到不公平和公平的呢?什么是可重入锁?公平锁与非公平锁FairLockVsUnfairLock
当你掌握了ReentrantLock加锁后,如果加锁入队失败,释放锁的原理其实还需要做在ReenrantLock了解几个概念,排他锁,共享锁,可重入锁,公平锁和非公平锁。这节先说公平锁和非公平锁。什么是公平锁?再说非公平锁呢?这里给大家举个例子:相信你一定有过排队的经历吧,比如你排队给女朋友买奶茶。但是你排好,突然一个老板的亲戚或者相关的家庭过来了加入一个队列是什么感觉?是不是觉得不公平?不过有的相关户也是很有修养的。就是公平锁和非公平锁的意思。大家可以想一想,还是上面的例子,线程2在排队,而线程1这时候释放了锁,但是突然有个线程3来加锁,会不会在线程2出队的过程中,线程3抢到了锁,不公平。线程3跳入队列,不老老实实排队。但是如果线程3老老实实排队,进入AQS队列,那么就是公平锁。如下图所示:ReentrantLock是如何做到不公平和公平的?
ReentrantLock是如何做到不公平和公平的? 具体代码是怎么做的呢?核心是通过两个Sync子类FairSync和NonfairSync。看名字就应该知道这两个类是fair和unfairAQS的Sync组件。大家可以把它们两个类的找不一样看:staticfinalclassNonfairSyncextendsSync{privatestaticfinallongserialVersionUID=7316153563782823691L;finalvoidlock(){if(compareAndSetState(0,1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}protectedfinalbooleantryAcquire(intacquires){returnnonfairTryAcquire(acquires);}}finalbooleannonfairTryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();intc=getState();if(c==0){if(compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}elseif(current==getExclusiveOwnerThread()){intnextc=c+acquires;if(nextc<0)//溢出thrownewError("Maximumlockcountexceeded");setState(nextc);returntrue;}returnfalse;}staticfinalclassFairSyncextendsSync{privatestaticfinallongserialVersionUID=-3000897897090466540L;finalvoidlock(){ac要求(1);}protectedfinalbooleantryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();intc=getState();if(c==0){if(!hasQueuedPredecessors()&&compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}elseif(current==getExclusiveOwnerThread()){intnextc=c+acquires;if(nextc<0)thrownewError("超过最大锁计数");setState(nextc);returntrue;}returnfalse;多了一个判断,先尝试加锁的区别是什么意思?你可以这样理解,如果线程1释放了,又有人来加锁,先尝试插入一个队列。有可能AQS队列中的线程2还没有被唤醒,锁被别人拿走了,所以其他线程可以加进去。锁定成功。如何释放锁,另外就是如果锁完全释放了,如何唤醒队头线程尝试获取锁。还有一个方法,尝试加锁,唯一不同的是一个if条件hasQueuedPredecessors()这个方法从名字就可以看出,判断队列中是否有元素。代码如下:publicfinalbooleanhasQueuedPredecessors(){Nodet=tail;//以相反的初始化顺序读取字段Nodeh=head;节点;returnh!=t&&((s=h.next)==null||s.thread!=Thread.currentThread());}也就是说公平锁尝试加锁的代码中,有一个限制,如果有人排队,其他线程不能跳入队列加锁。所以即使线程1释放锁,线程3来加锁,由于lock方法没有非公平锁的if(最多尝试CAS修改状态,加锁代码),线程3也只能加入queue,如果线程3执行到try获取锁的代码时,公平锁比非公平锁的代码多了一个判断,就是判断队列中是否有等待线程。有的话只能乖乖排队。如下图:可重入锁与不可重入锁
可重入锁与不可重入lock 如前所述,ReentrantLock涉及到锁的一些概念。说完公平锁和非公平锁的概念,今天终于要说说可重入锁了。其实这个还是比较容易理解的。ReentrantLock巧妙的通过AQS的状态变量实现了可重入锁。.如果同一个线程调用lock方法并添加锁,状态将在现有值上加+1。每次加锁都是可重入的,所以锁是可以重入的。也就是说:同一个线程可以使用同一个ReentrantLock进行重复加锁。此外,如果释放锁,则必须释放多次。同一个线程多次加锁,需要多次释放,需要将状态值恢复为0才能真正释放锁,其他线程才能获取。因为比较简单,就不给大家看源码实现了。你可以自己在源代码中找到它。核心是掌握AQS加锁和释放锁的原理。独占锁VS共享锁