Java并发之ReentrantLock原理详解
2018-02-12 12:13
597 查看
一、ReentrantLock是什么?
ReentrantLock是Lock接口的默认实现,是一种独占锁。相对synchronized而言,ReentrantLock提供了更多的操作方式以及更细粒度的加锁方式。主要特性:
(1)可重入。ReentrantLock是可重入锁,因为它会记录之前获得锁线程对象,保存在exclusiveOwenerThread变量中,当一个线程要获取锁时,会先判断当前线程是不是已经获取锁的线程。synchronized也是可重入锁。
(2)可中断。ReentrantLock是可中断锁,它提供了lockInterruptibly这种可中断的加锁方式,可以有效的避免线程之间因为互相持续占有资源而导致阻塞。synchronized无法实现可中断。
(3)公平锁与非公平锁可选。ReentrantLock默认是公平锁,但是也可以通过构造方法选择非公平锁。公平锁是指当多个线程尝试获取同一个锁时,获取锁的顺序按照到达的时间顺序排序。
二、ReentrantLock的底层原理。
ReentrantLock是继承了队列同步器AQS,关于AQS的原理,我之前写过博客,链接为点击打开链接。我们先要了解一下ReentrantLock的两个构造方法。 public ReentrantLock() {
sync = new NonfairSync();//默认为不公平锁
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();//若为true,则是公平锁
}ReentrantLock通过构造器参数选择到底是公平模式还是非公平模式。
再通过一张类关系图看一下NonfairSync和FairSync这两个内部类的来头。
可以看到NonfairSync和FairSync都是继承了Sync这个抽象类,而Sync则继承了AQS。Sync、NonfairSync、FairSync都是ReentrantLock的静态内部类,ReentrantLock的许多方法都是Sync类代为实现。
再看一下关键的最为关键的lock方法。public void lock() {
sync.lock();//sync可能是NonfairSync或者FairSync
}很简单的一个方法实现,sync代为实现lock的逻辑,而sync是Sync的实例,对于ReentrantLock来说,它的代码不需要知道Sync到底是NonfariSync还是FairSync,在运行时才会知道。这就是设计模式中的策略模式。
1、接下来先来看NonfairSync的lock方法。final void lock() {
if (compareAndSetState(0, 1)) //先通过CAS操作尝试获取锁,即把state从0变为1
setExclusiveOwnerThread(Thread.currentThread()); //若获取锁成功,则设置独占的线程为当前线程
else
acquire(1); //若无法通过上述操作获得锁,则要通过AQS的acquire操作来再次获取锁,若还未成功则进入等待队列
}acquire方法在AQS中已经提供实现不需要重写。关于AQS的讲解在这个链接的博客。
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }关键是需要NonfairSync自己实现的tryAcquire。protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);//调用了父类的方法
}
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread();//先获取当前线程对象 int c = getState(); //获取state的值 if (c == 0) { //若state为0,则可以通过cas方式获取锁 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); //将当前线程设置为独占线程 return true; } } else if (current == getExclusiveOwnerThread()) { //若state=1,则先看当前线程是否和已经独占的线程相等 int nextc = c + acquires; //如果是已经获取锁的线程,则修改state if (nextc < 0) // overflow 判断state的合法性 throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }2、现在我们来看一下公平锁下的acquire方法。
final void lock() { acquire(1); }很简单,不会先尝试抢占锁,直接调用acquire方法,这就是公平锁的公平之处。acquire方法就不说了,在AQS中已经写好了,Sync也没有重写这个方法,直接看acquire中的tryAcquire方法。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;
}这个公平锁的tryAcquire方法与非公平锁的tryAcquire方法只差了一点点,那就是当没有线程占有锁时,不会在队列中有等待线程存在的情况下去尝试抢占锁,这也是保证公平的必要条件,就是乖乖排队。
3、释放锁的方法unlock。public void unlock() {
sync.release(1);
}也只是简单的调用了一下Sync的release方法,释放方法不存在公平与非公平的区别,这个方法就是AQS中已经实现的方法。public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}主要说一下tryRelease方法,该方法也不存在公平与非公平的区别。protected final boolean tryRelease(int releases) {
int c = getState() - releases;//state值变化
if (Thread.currentThread() != getExclusiveOwnerThread())//若当前的线程与独占线程不是同一个,则抛出异常
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//若释放锁后,没有线程占用锁了
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
相关文章推荐
- 并发编程学习总结(六) :java 显式锁ReentrantLock使用详解之测试锁与超时
- Java并发之ReentrantLock详解
- java并发ReentrantLock原理剖析
- 【Java并发编程】15、ReentrantLock实现原理深入探究
- 【Java并发】- ReentrantReadWriteLock,读写锁原理
- Java并发——重入锁ReentrantLock的实现原理及源码解析
- java并发锁ReentrantLock源码分析一 可重入支持中断锁的实现原理
- java并发编程之:ReentrantLock实现原理与深入研究
- [置顶] Java并发之ReentrantLock详解
- java并发控制:ReentrantLock Condition使用详解
- java多线程:并发包中ReentrantLock锁的公平锁原理
- 并发编程学习总结(五) :java 显式锁ReentrantLock使用详解之条件对象(2)
- java并发控制:ReentrantLock Condition使用详解
- java多线程:并发包中ReentrantReadWriteLock读写锁的原理
- java并发控制:ReentrantLock Condition使用详解
- 并发编程学习总结(四) :java 显式锁ReentrantLock使用详解之lock()\unlock() 加锁与释放锁
- 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁
- JAVA并发编程学习笔记之ReentrantLock (r)
- Java并发包中Lock的实现原理
- 【死磕Java并发】-----J.U.C之读写锁:ReentrantReadWriteLock