作者专注于Java、架构、Linux、小程序、爬虫、自动化等技术。 工作期间含泪整理出一些资料,微信搜索【程序员高手之路】,回复 【java】【黑客】【爬虫】【小程序】【面试】等关键字免费获取资料。


目录

​一、整体结构​

​二、三个内部类​

​1.Sync​

​2.NonfairSync​

​3.FairSync​

​三、主要方法​

​1.lock()方法​

​2.tryLock()方法​

​3.unlock()方法​

​4.lockInterruptibly()方法​

​四、其它​

​1.公平锁与非公平锁的区别​

​2.重入锁的实现原理​


一、整体结构

该类位于java.util.concurrent.locks包下

首先看一下结构:

架构系列——通过ReentrantLock源码分析给对象上锁的原理_实现原理

ReentrantLock中:

3个类:Sync、NonfairSync、FairSync

若干个方法:lock()、tryLock()、unlock()、acquireInterruptibly(int arg)等

架构系列——通过ReentrantLock源码分析给对象上锁的原理_公平锁_02

具体流程 

架构系列——通过ReentrantLock源码分析给对象上锁的原理_java_03

二、三个内部类

1.Sync

abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;

//抽象lock方法
abstract void lock();

//该TryAquire默认给非公平锁使用
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//判断锁的状态
int c = getState();
//锁为空闲
if (c == 0) {
//进行CAS操作,设置当前线程持有锁
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;
}

//释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//释放锁的线程必须为持有锁的线程
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//state为0才能释放锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}

//如果当前线程同步是以独占的方式进行的,则返回true
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}

//新生成的条件
final ConditionObject newCondition() {
return new ConditionObject();
}

// Methods relayed from outer class
//获得占用同步状态的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}

//获得当前线程持有锁的数量,即重入次数
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}

//是否被锁定
final boolean isLocked() {
return getState() != 0;
}

//自定义饭序列化逻辑
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}

2.NonfairSync

static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;

//首先基于AQS进行CAS操作,将0->1.若成功,则获取锁成功
//否则,执行AQS的正常同步状态获取逻辑
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}

3.FairSync

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;
}
}

三、主要方法

注:流程图省略了其他相关类的一些方法,只讲ReentrantLock类里面的方法,比如AQS(AbstractQueuedSynchronizer)类里面的acquere方法中省略了acquireQueued()selfInterrupt()。

如果tryAcquire(arg)返回成功,则说明当前线程成功获取了锁(第一次获取或者重入),由取反和&&可知,整个流程到这结束,只有当前线程获取锁失败才会执行后面的判断。addWaiter(Node.EXCLUSIVE)

部分代码描述了当线程获取锁失败时如何安全的加入同步等待队列。这部分代码可以说是整个加锁流程源码的精华,充分体现了并发编程的艺术性。

架构系列——通过ReentrantLock源码分析给对象上锁的原理_公平锁_04

1.lock()方法

public void lock() {
sync.lock();
}

1.1 如果该lock是NonfairSync类的实例,则进入NonfairSync的lock()里:

最底层还是进入nonfairTryAcquire()方法里

架构系列——通过ReentrantLock源码分析给对象上锁的原理_实现原理_05

1.2 如果该lock是FairSync类的实例,则进入FairSync的lock()里:

最底层是进入tryAcquire()方法里

架构系列——通过ReentrantLock源码分析给对象上锁的原理_实现原理_06

2.tryLock()方法

public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}

一步到位,直接调用nonfairTryAcquire()方法

架构系列——通过ReentrantLock源码分析给对象上锁的原理_公平锁_07

3.unlock()方法

public void unlock() {
sync.release(1);
}

架构系列——通过ReentrantLock源码分析给对象上锁的原理_实现原理_08

4.lockInterruptibly()方法

public void lockInterruptibly() throws InterruptedException {
//调用父类AbstractQueuedSynchronizer里的方法
sync.acquireInterruptibly(1);
}

//这是父类AbstractQueuedSynchronizer的方法
public final void acquireInterruptibly(int arg)
throws InterruptedException {
//中断线程,如果成功中断,则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}

这是Lock锁和synchronized锁相比独有的特性之一,获取锁的线程是可以被中断的,而且中断的时候抛出异常;

四、其它

由上面的图可知,ReentrantLock里面的最最基本的方法是tryAcquire()nonfairTryAcquire()tryRelease()

把这两个方法搞懂了,这个类也就明白很大一部分了

另外要理解两点:

1.公平锁与非公平锁的区别

FairSync中整个tryAcquire()代码只比nonfairTryAcquire()方法多了这么一行!!!

主要判断:是否有其它线程在队列的最前面。

以此来保证有序性,也就是先来后到!

这就是公平!

而不公平就是可以插队拿到锁!

架构系列——通过ReentrantLock源码分析给对象上锁的原理_java_09

架构系列——通过ReentrantLock源码分析给对象上锁的原理_java_10

2.重入锁的实现原理

1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;

2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功

final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();//获取当前线程实例
int c = getState();//获取state变量的值,即当前锁被重入的次数
if (c == 0) { //state为0,说明当前锁未被任何线程持有
if (compareAndSetState(0, acquires)) { //以cas方式获取锁
setExclusiveOwnerThread(current); //将当前线程标记为持有锁的线程
return true;//获取锁成功,非重入
}
}
else if (current == getExclusiveOwnerThread()) { //当前线程就是持有锁的线程,说明该锁被重入了
int nextc = c + acquires;//计算state变量要更新的值
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);//非同步方式更新state值
return true; //获取锁成功,重入
}
return false; //走到这里说明尝试获取锁失败
}