当前位置: 首页 > 后端技术 > Java

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共享锁独占锁VS共享锁
关于排他锁和共享锁的概念,我们再解释一下读写锁的概念我会在这里简单地提一下。所谓排他锁就是只要有一个线程加锁了,其他人都得靠边站。这个锁排他性的属于某个线程,就是独占锁。reentrantLock.lock默认创建的锁是什么?不公平的可重入独占锁!共享锁是什么意思?意思是可以和其他线程同时持有一把锁,比如后面要用到的读写锁。线程1加读锁,线程2还可以加读锁,他们共享一把锁。这样的锁就是共享锁。当然,读写锁之间也存在一些互斥关系,所以下一节我们将探讨一下读写锁的使用方法,读写锁的原理是什么,以及读写之间的互斥关系锁。总结与思考总结与思考这部分其实不是特别负责复杂的知识,主要我想带大家看看ReentrantLock的可重入实现的核心思想是通过代码执行顺序、CAS操作顺序和一个if来判断队列中是否有等待线程。二是介绍几个概念,可重入锁,公平锁,非公平锁,排他锁和共享锁分别是什么意思?其实这里已经大致分析了ReentrantLock的原理。你至少掌握了ReentrantLock基于3个组件(变量)抽象类封装的运行设计。我们之前提到的synchronized底层ObjectMonitor其实就是按照这个思路设计的,只不过一个是C++封装的ObjectMonitor对象,一个是Java封装的ReentrantLock对象。当你学会了一项技术的原理和源代码,或者完成了一个项目之后,你必须学会??思考。只有经过思考,才能更好地应用这项技术,更好地解决问题。另外有兴趣的同学可以深入了解CAS的底层JVMC++语言是如何实现的,为什么要使用LockSupport.park挂起线程,LockSupport的park方法的实现等等。.下一节我们就开始研究ReentrantReadWriteLock的实现原理,下节见!本文由博客多发平台OpenWrite发布!