1.定义

ReentrantLock是一种可重入锁,允许一个线程对同一个资源重复加锁,如果说是当一个线程调用一个锁的lock()方法,然后再次调用锁的lock()方法,当锁不支持可重入时,该线程会被自己所阻塞。该锁还能支持公平锁和非公平锁的选择,公平的意思就是将锁分配给等待锁时间最长的线程,这样可以避免饥饿情况的发生,但是造成线程大量的上下文切换,影响吞吐量。

从ReentrantLock的类定义来看,该锁实现了Lock和Serializable接口。

public class ReentrantLock implements Lock, java.io.Serializable

2.ReentrantLock的具体实现

ReentrantLock通过将继承AQS的子类sync作为类成员变量来实现锁,sync实现AQS的抽象方法来管理同步状态。

(1)Sync静态内部类

  //AQS的子类。
private final Sync sync; //Sync也是一个抽象类,因为锁有非公平和公平的区别。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L; //非公平和公平锁的lock()方法有不同的实现。
abstract void lock();

//tryLock()在子类中实现,该方法是非公平的独占式获取同步状态。
//该方法首先判断同步状态是否被获取,如果没有,CAS获取同步状态并将锁的拥有者设为当前线程,如果有,判断获取锁的线程是否是当前线程,如果是,将同步值进行累加。
//成功获取锁的线程,再次获取锁,只是增加了同步值。
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;
}

//获取了多少次锁,同样也要释放多少次锁。
//当同步值不为0时,还是当前线程占有锁。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}

//是否被当前线程所占有
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
} final ConditionObject newCondition() {
return new ConditionObject();
} //得到锁的占有者
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和FairSync

ReentrantLock是支持公平锁和非公平锁的,而公平锁和非公平锁的具体实现是依靠AQS的抽象子类Sync的不同子类实现(NonfairSync和FairSync)。

    //非公平锁的实现
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L; //非公平锁加锁,acquire调用tryAcquire,tryAcquire调用nonfairTryAcquire
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
} protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
} //公平锁的实现
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L; final void lock() {
acquire(1);
} //lock()调用acquire(),acquire()调用tryAcquire(),公平锁和非公平锁的tryAcquire()的实现唯一不同
//就是加入了hasQueuesPredecessors(),通过判断当前节点是否有前驱节点,如果有则当前节点不是等待时间最长的线程
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;
}
}

公平锁和非公平锁的主要在于两个地方:

1.获取锁lock()方法的不同:非公平锁会直接进行一次CAS,如果CAS成功,就直接获取锁了。

2.tryAcquire()方法实现的不同:当同步状态此时被释放的时候,state等于0,并没有任何线程占有锁,如果是公平锁,会判断队列中是否有线程等待获取锁,如果有的话,直接将自己构造成一个节点加入同步队列,但是非公平锁会直接CAS设置状态,不考虑同步队列中是否有其他线程等待获取锁。如果获取同步状态失败,会将自己构造成一个节点加入同步队列中,加入同步队列之后,等待前驱节点释放同步状态,唤醒自己,这后面的步骤公平锁和非公平锁是一样的。

3.ReentrantLock的主要方法

  //构造函数默认是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
} //可选择实现公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
} //获取锁
public void lock() {
sync.lock();
} //可中断式的获取锁
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
} //尝试非阻塞的获取锁,调用方法之后立马返回,能获取返回true,不能获取返回false
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
} //释放锁
public void unlock() {
sync.release(1);
} //获取等待通知组件,该组件和当前锁绑定
public Condition newCondition() {
return sync.newCondition();
} //锁被获取的次数
public int getHoldCount() {
return sync.getHoldCount();
} //锁是否被当前线程所占有
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
} //锁是否已经被获取
public boolean isLocked() {
return sync.isLocked();
} //是否是公平锁
public final boolean isFair() {
return sync instanceof FairSync;
} //获取锁的所有者
protected Thread getOwner() {
return sync.getOwner();
} //是否有线程等待该锁
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
} //查询给定线程是否在等待该锁
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
} //查询等待锁的线程数
public final int getQueueLength() {
return sync.getQueueLength();
} //返回等待锁的线程集合
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}

4.ReentrantLock总结

聚合关系总结:

(1)ReentrantLock实现了Lock,Serializable接口

(2)ReentrantLock.Sync(内部类)继承了AQS

(3)ReentrantLock.NonfairSync和ReentrantLock.FairSync继承了ReentrantLock.Sync

(4)ReentrantLock持有ReentrantLock.Sync对象(实现锁功能)

性质:

(1)独占锁(排它锁):只能有一个线程获取锁

(2)重入锁:一个线程可以多次lock()

(3)公平/非公平锁:只针对上锁过程

(4)非公平锁:尝试获取锁,若成功立刻返回,失败则加入同步队列

(5)公平锁:直接加入同步队列

 

JDK源码之ReentrantLock的更多相关文章

  1. 【JDK】JDK源码分析-ReentrantLock

    概述 在 JDK 1.5 以前,锁的实现只能用 synchronized 关键字:1.5 开始提供了 ReentrantLock,它是 API 层面的锁.先看下 ReentrantLock 的类签名以 ...

  2. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  3. [源码分析]ReentrantLock & AbstractQueuedSynchronizer & Condition

    首先声明一点: 我在分析源码的时候, 把jdk源码复制出来进行中文的注释, 有时还进行编译调试什么的, 为了避免和jdk原生的类混淆, 我在类前面加了"My". 比如把Reentr ...

  4. 【并发编程】【JDK源码】J.U.C--AQS (AbstractQueuedSynchronizer)(1/2)

    J.U.C实现基础 AQS.非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),concurrent包中的基础类都是使用这种模式来实现的.而concurren ...

  5. 【并发编程】【JDK源码】J.U.C--AQS 及其同步组件(2/2)

    原文:慕课网高并发实战(七)- J.U.C之AQS 在[并发编程][JDK源码]AQS (AbstractQueuedSynchronizer)(1/2)中简要介绍了AQS的概念和基本原理,下面继续对 ...

  6. JDK源码阅读顺序

      很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Obj ...

  7. JDK源码分析之concurrent包(二) -- 线程池ThreadPoolExecutor

    上一篇我们简单描述了Executor框架的结构,本篇正式开始并发包中部分源码的解读. 我们知道,目前主流的商用虚拟机在线程的实现上可能会有所差别.但不管如何实现,在开启和关闭线程时一定会耗费很多CPU ...

  8. 【JDK】JDK源码分析-AbstractQueuedSynchronizer(1)

    概述 前文「JDK源码分析-Lock&Condition」简要分析了 Lock 接口,它在 JDK 中的实现类主要是 ReentrantLock (可译为“重入锁”).ReentrantLoc ...

  9. 【JDK】JDK源码分析-AbstractQueuedSynchronizer(2)

    概述 前文「JDK源码分析-AbstractQueuedSynchronizer(1)」初步分析了 AQS,其中提到了 Node 节点的「独占模式」和「共享模式」,其实 AQS 也主要是围绕对这两种模 ...

随机推荐

  1. NeuroNER+brat工具学习

    1.Brat:http://brat.nlplab.org/ 能够进行直觉标注.命名实体识别.关系标注.分块.共存标注.二元关系标注等(药物与药物).时间标注. 但是这个安装好麻烦啊... 2.

  2. ansible 快速入门

    安装 $ sudo apt-get install software-properties-common $ sudo apt-add-repository ppa:ansible/ansible $ ...

  3. [Java in NetBeans] Lesson 04. Class / Objects

    这个课程的参考视频和图片来自youtube. 主要学到的知识点有: Class: Blueprint for an object. (e.g. dog is a class) Object: cust ...

  4. 实践 : Ubuntu 上 Testlink 部署

    1.安装apache sudo apt-get install apache2 2. sudo /etc/init.d/apache2 restart 测试: Http:\localhost or I ...

  5. LightGBM调参总结

    1. 参数速查 使用num_leaves,因为LightGBM使用的是leaf-wise的算法,因此在调节树的复杂程度时,使用的是num_leaves而不是max_depth. 大致换算关系:num_ ...

  6. OGeek CTR预估

    特征工程:将所有数据拼接到一起做特征,等到处理完,再按索引拆分数据 1.数据分析 查看标签分布:发现valid与testa.testb的分布相似,说明valid与testa的查询时间比较接近,作为验证 ...

  7. Kotlin 范型约束

    官方的示意及其简约,该说的一概没说 我在这里给大家一个完整的例子 //test.kt fun <T> cloneWhenGreater(list: List<T>, thres ...

  8. jQuery-表格属性

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. eclipse中tomcat启动成功,浏览器访问失败

    eclipse添加tomcat之后,tomcat有个默认设置,我们需要对tomcat进行重新设置: 1.双击已添加的tomcat,进入到配置页面,找到server locations一栏,可以看到默认 ...

  10. python base64加密文本内容(1)

    仅仅使用base64加密安全系数太低了,我们还可以自定义base64加密后的文本进行一些变化,提高安全系数,在解密时再回复某些变化 1,先实现一个base64加密 import base64 impo ...