上一章你应该把握了Atomic的底层原理-CAS。接下来进入另一个重要的一个常识AQS。咱们通过ReentrantLock这个类来讲讲AQS这个常识。
从上图能够看出,ReentractLock、ReadWriteReentractLock,这些锁API底层是基于AQS+CAS+volatile来实现的,个别不会间接应用,常应用的是一些并发汇合API,然而它们的底层大多还是基于ReentrantLock或者AQS来实现的。
ReentrantLock属于java并发包里的底层的API,专门撑持各种java并发类的底层的逻辑实现。
ReenranctLock的内容比拟多,打算分6节来讲。
- 第一节讲一下初识ReenranctLock加锁的AQS底层原理
- 第二节讲一下ReenranctLock加锁入队的AQS底层原理
- 第三节讲一下ReenranctLock开释锁的底层原理
- 第四节讲一下ReenranctLock锁的可重入、偏心、非偏心
- 第五节讲一下ReentrantReadWriteLock读写锁的原理
- 第六节讲一下ReenranctLock中condition的利用
Hello 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;”>Hello ReentrantLock</span></h3></div>
很多人可能没有用过ReentrantLock,在一些并发状况下因为要保障一些原子性操作,可能也会用到。然而大多数人很少接触高并发的场景,所以用这个类的人可能很少。
然而在一些开源我的项目中还是有应用到的,比方Spring Cloud的Eureka组件。有时候面试也常常考AQS或者并发汇合的问题。所以把握ReentrantLock的原理是十分有必要的。这样你能够轻车熟路的了解它在开源我的项目的应用,更不会在面试的时候被问住。
第一点还是先来看个HelloWorld的例子。咱们为了保障某些操作同一时间只能有一个线程操作,会对整个操作加一个锁。除了synchronized之外,咱们还能够应用ReentrantLock。假如有一个操作是j++。
那么Hello ReentrantLock代码如下:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
static int j = 1;
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
for(int i=0;i<10;i++){
new Thread(()->{
reentrantLock.lock();
try{
System.out.println(Thread.currentThread().getName()+"-后果:"+j++);
}catch (Exception e){
}finally {
reentrantLock.unlock();
}
}).start();
}
}
}
从一张图先鸟瞰下ReentrantLock外围的3个小组件
<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外围的3个小组件</span></h3></div>
ReentrantLock外围有3个组件:state、owner、AQS(形象队列同步器,简略的说就是一个期待队列Queue)。如下图所示:
这里你能够先有个概念就行,你之后会具体的理解到这几个组件作用的。
从JDK源码层面找一下对应的组件
<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;”>从JDK源码层面找一下对应的组件</span></h3></div>
当你有了下面这张图的印象后,咱们通过Hello ReentrantLock来剖析下它的组件在源码里的体现。
首先还是简略看下ReentrantLock的源码脉络:
它次要脉络有:
1) 一些API办法
2) 3个外部类,Sync、NonfairSync、Sync
3) 1个成员变量Sync对象
理解了源码的大体脉络后,接下来,剖析下它的应用过程,首先必定是创立ReentrantLock,让咱们来看看构造函数做了些什么。代码如下:
public ReentrantLock() {
sync = new NonfairSync();
}
发现外部创立了对象,赋值给了sync变量。创立了一个外部类NonfairSync。持续深刻能够发现如下代码关系:
static final class NonfairSync extends Sync {
}
abstract static class Sync extends AbstractQueuedSynchronizer {
原来sync变量创立的对象是NonfairSync。它的父类是Sync,而这个类的父类是一个AbstractQueuedSynchronizer。
你能够猜测下,AbstractQueuedSynchronizer这个是什么货色?从名字上看叫做形象队列同步器,缩写是AQS。咿?这个就是之前提到的AQS啊。
认真看一下这个父类的脉络:
首先也是一对办法,然而变量很有意思,UnSafe类、head/tail+Node外部类?这让你想到了什么?
没错,上一节刚接触过的Aotmic类Unsafe能够用作CAS操作的类,head/tail+Node外部类这不是LinkedList的数据结构么?这个就是下面提到过ReentrantLock的3个小组件之一——期待队列Queue。
等等,还有一个int state。这个就是下面提到过ReentrantLock的3个小组件之一——state变量。你能够看到这几个变量对应的代码如下:
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
state和 期待队列都看到了,owner去哪里了?原来AQS还有一个父类。叫做AbstractOwnableSynchronizer。
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
}
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
protected AbstractOwnableSynchronizer() { }
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
下面这个exclusiveOwnerThread不就是独占的owner线程的意思么?原来在这里。这就是3个小组件中的最初一个组件-owner线程。
到这里你就能够失去如下所示的源码层面的组件图:
从JDK源码层面了解AQS的线程第一次加锁
<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;”>从JDK源码层面了解AQS的线程第一次加锁</span></h3></div>
当你有了ReentrantLock加锁过程的这个概念后,来剖析下源码就很简略多了。
lock办法源码如下:
public void lock() {
sync.lock();
}
间接调用了ReentrantLock的外部类,Sync组件的lock办法,而Sync组件lock办法是形象的。如下:
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
}
sync这变量,在之前的构造函数中,理论创立的是AbstractQueuedSynchronizer(AQS)的子类:NonfairSync。所以找到对应的lock办法代码如下:
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
首先进行的操作就是一个CAS,更新了volatile变量state,由0变为1。底层应用的是Unsafe类操作的。这个和Aotmic类的底层CAS没什么区别,是相似的。
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
之后如果没有别的线程并发进行CAS操作的话,这个批改state的CAS操作会胜利,并且返回true。接着就会执行setExclusiveOwnerThread办法了。这个办法代码如下:
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
理论就是设置了以后加锁的线程owner。接着整个lock办法就完结了。
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
最终你会失去如下所示的流程图:
小结&思考
<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就是基于3个属性来实现的,只不过是通过抽象类封装了公共的属性和操作,而这个抽象类常被咱们成为AQS。
第一次加锁其实次要就是
1、CAS操作一个volatile的int state从0->1
2、是指一个onwerThread为加锁线程
3、如果没有竞争的状况,和Queue队列没关系的。
大家学完一个技术后,肯定要一会思考和提炼关键点、形象思维,这个是十分重要的!
本文由博客一文多发平台 OpenWrite 公布!