什么是重入锁和AQS
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是重入锁和AQS相关的知识,希望对你有一定的参考价值。
参考技术A 什么是重入锁java.util.concurrent.locks.ReentrantLock
ReenTrantLock独有的能力:
1. ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。
2. ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
3. ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。
非重入锁
当A方法获取锁去锁住一段需要做原子性操作的B方法时,如果这段B方法又需要锁去做原子性操作,那么A方法就必定要与B方法出现死锁。这种会出现问题的重入一把锁的情况,叫不可重入锁。
lock的过程:
首先尝试获取资源,如果当前状态为0,表示没有线程占有锁,设置该线程为独占模式,使用CAS设置状态,否则如果当前线程和独占线程是一个线程,修改状态值,否则返回false。
若获取资源失败,则通过addWaiter <-(aqs)方法创建一个节点并放在CLH队列的尾部。head tail未初始化会创建虚拟节点同时指向
为什么 AQS 需要一个虚拟 head 节点
每个节点都需要设置前置Node 的 waitStatus 状态(这个状态为是为了保证数据一致性),防止重复释放操作。而第一个节点是没有前置节点的,所以需要创建一个虚拟节点。
逐步去执行CLH队列中的线程,当前线程会公平性的阻塞一直到获取锁为止,返回线程在等待的过程中还是否中断过。
unlock的过程
一次unlock操作需要修改状态位,然后唤醒节点。整个释放操作也是使用unpark()来唤醒队列最前面的节点。其实lock中比较重要的也就是lock和release,它们又和AQS联系紧密,下面会单独谈谈AQS的重要方法。
Condition的await和signal
wait和notify/notify VS await和signal
Condition能够支持不响应中断,而通过使用Object方式不支持;
Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个;
Condition能够支持超时时间的设置,而Object不支持
对标Object的wait方法
void await() throws InterruptedException:当前线程进入等待状态,如果其他线程调用condition的signal或者signalAll方法并且当前线程获取Lock从await方法返回,如果在等待状态中被中断会抛出被中断异常;
long awaitNanos(long nanosTimeout):当前线程进入等待状态直到被通知,中断或者超时;
boolean await(long time, TimeUnit unit)throws InterruptedException:同第二种,支持自定义时间单位
boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态直到被通知,中断或者到了某个时间
对标Object的notify/notifyAll方法
void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队列中能够竞争到Lock则可以从等待方法中返回。
void signalAll():与1的区别在于能够唤醒所有等待在condition上的线程
如图所示,ConditionObject是AQS的内部类,因此每个ConditionObject能够访问到AQS提供的方法,相当于每个Condition都拥有所属同步器的引用。
调用condition.await方法的线程必须是已经获得了lock,也就是当前线程是同步队列中的头结点。调用该方法后会使得当前线程所封装的Node尾插入到等待队列中。
如图,线程awaitThread先通过lock.lock()方法获取锁成功后调用了condition.await方法进入等待队列,而另一个线程signalThread通过lock.lock()方法获取锁成功后调用了condition.signal或者signalAll方法,使得线程awaitThread能够有机会移入到同步队列中,当其他线程释放lock后使得线程awaitThread能够有机会获取lock,从而使得线程awaitThread能够从await方法中退出执行后续操作。如果awaitThread获取lock失败会直接进入到同步队列。
// 线程已被取消
static final int CANCELLED = 1;
// 当前线程的后继线程需要被unpark(唤醒)
// 一般发生情况是:当前线程的后继线程处于阻塞状态,而当前线程被release或cancel掉,因此需要唤醒当前线程的后继线程。
static final int SIGNAL = -1;
// 在Condition休眠状态,在等待Condition唤醒
static final int CONDITION = -2;
// (共享锁)其它线程获取到“共享锁”,对应的waitStatus的值
static final int PROPAGATE = -3;
volatile int waitStatus;
---------------------
/**
* 这个方法也就是lock()方法的关键方法。tryAcquire获得资源,返回true,直接结束。若未获取资源,新建一个节点插入队尾,
*addWaiter用于添加节点,也就是把当前线程对应的节点插入CLH队列的尾部。
* @param arg the acquire argument. This value is conveyed to
* @link #tryAcquire but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg)
if (!tryAcquire(arg) &&//获取资源立刻结束
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//没有被中断过,也结束
selfInterrupt();
---------------------
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;
---------------------
* 非公平锁
*/
static final class NonfairSync extends Sync
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock()
if (compareAndSetState(0, 1))//CAS设置当前为0 的时候上锁
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);
/**
*
*/
protected final boolean tryAcquire(int acquires)
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0)
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) //没有前驱节点并且CAS设置成功
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;
以上是关于什么是重入锁和AQS的主要内容,如果未能解决你的问题,请参考以下文章