通过后面的三节,置信你对 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 公布!