通过后面的三节,置信你对ReentrantLock底层的AQS原理曾经很分明了。接下来给大家介绍几个ReentrantLock中的几个概念:

  • 偏心,非偏心锁的概念
  • ReentrantLock是如何实现非偏心和偏心的?
  • 可重入锁又是什么货色?

偏心锁 Vs 非偏心锁

<div class="output_wrapper" id="output_wrapper_id" style="width:fit-content;font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"><h3 id="hdddd" style="width:fit-content;line-height: inherit; margin: 1.5em 0px; font-weight: bold; font-size: 1.3em; margin-bottom: 2em; margin-right: 5px; padding: 8px 15px; letter-spacing: 2px; background-image: linear-gradient(to right bottom, rgb(43,48,70), rgb(43,48,70)); background-color: rgb(63, 81, 181); color: rgb(255, 255, 255); border-left: 10px solid rgb(255,204,0); border-radius: 5px; text-shadow: rgb(102, 102, 102) 1px 1px 1px; box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">偏心锁 Vs 非偏心锁</span></h3></div>

当你把握了ReentrantLock加锁,加锁失败入队,开释锁的原理后。其实在ReenrantLock中还须要搞明确几个概念,比方独占锁、共享锁、可重入锁,偏心锁和非偏心锁这些都是什么意思。

这一大节,咱们先来聊聊偏心和非偏心锁。

什么是偏心锁?什么又是非偏心锁呢?这里给大家举个例子:

置信你必定有过排队的经验,比方你给女朋友排队买过奶茶。然而你排队排的好好的,忽然当有个老板亲戚或者关系户过去插了一个队,你是什么感觉?是不是感觉不太偏心。然而有的关系户也很有涵养,不会插队,会老老实实去排队,这就很偏心了, 因为先来后到么。

这其实就是偏心和非偏心的锁的意思。你能够想想,还是下面的例子,线程2在排队了,此时线程1开释了锁,可是忽然来了一个线程3,也来加锁,是不是可能在线程2出队的过程中,线程3抢到锁,这就是非偏心的,线程3插队了,没有老老实实排队。

然而如果线程3,老老实实的排队,进入AQS的队列中,这样就是偏心锁。如下图所示:

ReentrantLock是如何实现非偏心和偏心的?

<div class="output_wrapper" id="output_wrapper_id" style="width:fit-content;font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"><h3 id="hdddd" style="width:fit-content;line-height: inherit; margin: 1.5em 0px; font-weight: bold; font-size: 1.3em; margin-bottom: 2em; margin-right: 5px; padding: 8px 15px; letter-spacing: 2px; background-image: linear-gradient(to right bottom, rgb(43,48,70), rgb(43,48,70)); background-color: rgb(63, 81, 181); color: rgb(255, 255, 255); border-left: 10px solid rgb(255,204,0); border-radius: 5px; text-shadow: rgb(102, 102, 102) 1px 1px 1px; box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">ReentrantLock是如何实现非偏心和偏心的?</span></h3></div>

具体代码是怎么做到呢?外围是通过两个Sync的子类。FairSync和NonfairSync。从名字上看,你就应该晓得,这两个类是偏心和非偏心AQS的Sync组件意思。

大家能够它们两个类的找找不同看看:

static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() {    if (compareAndSetState(0, 1))      setExclusiveOwnerThread(Thread.currentThread());    else      acquire(1); } protected final boolean tryAcquire(int acquires) {    return nonfairTryAcquire(acquires); }}final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {    if (compareAndSetState(0, acquires)) {      setExclusiveOwnerThread(current);      return true;    } } else if (current == getExclusiveOwnerThread()) {    int nextc = c + acquires;    if (nextc < 0) // overflow      throw new Error("Maximum lock count exceeded");    setState(nextc);    return true; } return false;}
static final class FairSync extends Sync {  private static final long serialVersionUID = -3000897897090466540L;  final void lock() {    acquire(1);  }   protected final boolean tryAcquire(int acquires) {    final Thread current = Thread.currentThread();    int c = getState();    if (c == 0) {      if (!hasQueuedPredecessors() &&        compareAndSetState(0, acquires)) {        setExclusiveOwnerThread(current);        return true;      }    }    else if (current == getExclusiveOwnerThread()) {      int nextc = c + acquires;      if (nextc < 0)        throw new Error("Maximum lock count exceeded");      setState(nextc);      return true;    }    return false;  }}

首先是lock办法,区别就是在一个if判断,非偏心的锁NonfairSync会多了一个判断,先尝试来加个锁。

这个区别是什么意思呢?你能够了解为如果线程1开释了,他人过去加锁,间接先尝试插个队的意思,有可能AQS队列中的线程2还没被唤醒了,被他人抢走了锁,让别的线程加锁胜利了。

如何把锁给开释掉,另外一个是如果锁彻底开释了当前,如何让队列中的队头的那个线程来唤醒尝试获取锁。

而另一个办法,尝试加锁,惟一的区别是一个if条件

hasQueuedPredecessors()

这办法从名字就能看进去,判断下队列中有没有有元素。代码如下:

 public final boolean hasQueuedPredecessors() {  Node t = tail; // Read fields in reverse initialization order  Node h = head;  Node s;  return h != t &&   ((s = h.next) == null || s.thread != Thread.currentThread());}

也就是说,在偏心锁的尝试加锁的代码中,有一个限度如果有人排队,其余线程就不能插队加锁。所以就算线程1开释锁,线程3过去加锁,因为lock办法没有了非偏心锁的if(上来尝试CAS批改state,加锁的代码),线程3就只能入队,如果线程3执行到尝试获取锁的代码时,偏心锁比非偏心锁的代码多了一个判断,判断队列中是否有期待线程。有的话也只能乖乖排队。如下图所示:

可重入锁 Vs 不可重入锁

<div class="output_wrapper" id="output_wrapper_id" style="width:fit-content;font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"><h3 id="hdddd" style="width:fit-content;line-height: inherit; margin: 1.5em 0px; font-weight: bold; font-size: 1.3em; margin-bottom: 2em; margin-right: 5px; padding: 8px 15px; letter-spacing: 2px; background-image: linear-gradient(to right bottom, rgb(43,48,70), rgb(43,48,70)); background-color: rgb(63, 81, 181); color: rgb(255, 255, 255); border-left: 10px solid rgb(255,204,0); border-radius: 5px; text-shadow: rgb(102, 102, 102) 1px 1px 1px; box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">可重入锁 Vs 不可重入锁</span></h3></div>

之前提到,ReentrantLock波及了一些锁的概念,讲过了偏心和非偏心锁的概念后,明天咱们最初聊一下可重入锁。

其实这个比拟好了解,ReentrantLock通过AQS的state变量奇妙的实现了可重入加锁。如果是同一个线程调用了lock办法,加锁,state会在现有值上加+1,每再次加一次锁,就是一次可重入,所以就加锁可重入锁。也就是说:

同一个线程能够应用同一个ReentrantLock进行重复加锁。

另外,开释锁的话,必定须要开释所屡次,同一个线程加锁了几次,就须要开释几次,须要将state值复原为0才算真正的开释锁,别的线程能力获取到。

因为比较简单,就不带大家看源码实现了。你能够本人在源码中找找。外围还是把握AQS加锁开释锁的原理最重要。

独占锁 VS 共享锁

<div class="output_wrapper" id="output_wrapper_id" style="width:fit-content;font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"><h3 id="hdddd" style="width:fit-content;line-height: inherit; margin: 1.5em 0px; font-weight: bold; font-size: 1.3em; margin-bottom: 2em; margin-right: 5px; padding: 8px 15px; letter-spacing: 2px; background-image: linear-gradient(to right bottom, rgb(43,48,70), rgb(43,48,70)); background-color: rgb(63, 81, 181); color: rgb(255, 255, 255); border-left: 10px solid rgb(255,204,0); border-radius: 5px; text-shadow: rgb(102, 102, 102) 1px 1px 1px; box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">独占锁 VS 共享锁</span></h3></div>

至于独占和共享锁的概念之后解说读写锁的时候会提到。这里先简略的讲一下。

所谓独占锁,就是只有有一个线程加锁,其他人都得靠边站,这把锁属于某个线程独占,这就是独占锁。

默认reentrantLock.lock创立的锁是什么的呢?非偏心的可重入独占锁!

共享锁是什么意思呢?意思就是能够和别的线程同时持有一把锁,比方之后要将的读写锁。线程1加了读锁,线程2还是能够加读锁的,它们共享一把锁。这样的锁就是一把共享锁。

当然读写锁之间是有一些互斥关系的,所以下一节咱们就来摸索下,如何应用读写锁、读写锁的原理具体是什么、以及读写锁的互斥关系。

小结&思考

<div class="output_wrapper" id="output_wrapper_id" style="width:fit-content;font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"><h3 id="hdddd" style="width:fit-content;line-height: inherit; margin: 1.5em 0px; font-weight: bold; font-size: 1.3em; margin-bottom: 2em; margin-right: 5px; padding: 8px 15px; letter-spacing: 2px; background-image: linear-gradient(to right bottom, rgb(43,48,70), rgb(43,48,70)); background-color: rgb(63, 81, 181); color: rgb(255, 255, 255); border-left: 10px solid rgb(255,204,0); border-radius: 5px; text-shadow: rgb(102, 102, 102) 1px 1px 1px; box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">小结&思考</span></h3></div>

其实这一节并没有什么特地负责简单的常识,次要带大家看了 ReentrantLock的重入实现

核心思想就是通过代码执行程序,CAS操作程序和一个if判断队列是否有期待线程实现的。

其次就是介绍了几个概念,可重入锁、偏心、非偏心锁、独占和共享锁别离是什么意思。

ReentrantLock的原理其实到这里咱们大体就剖析实现了,你起码把握了 ReentrantLock的基于抽象类封装3个组件(变量)的操作设计。

之前咱们提到的synchronized的底层ObjectMonitor,其实是不是也是用过这个思维设计的,只不过一个是C++封装的ObjectMonitor对象,一个是Java封装的ReentrantLock对象。

大家当学完一个技术的原理和或者源码,或者做完一个我的项目后,肯定要学会进行思考,思考之后能力更好的利用这个技术、更好的解决问题。

另外有趣味的同学能够去深究下,它应用的CAS底层JVM C++语言如何的实现,为什么用LockSupport.park挂起线程,LockSupport的park办法实现等等。。

下一节咱们开始钻研下ReentrantReadWriteLock的实现原理,咱们下一节见!

本文由博客一文多发平台 OpenWrite 公布!