目录
- AQS简介
- AQS同步队列结构
- AQS获取同步状态的流程图
- ReentrantLock
- ReentrantLock类图
- ReentrantLock运用demo
- 公平锁(FairSync)
- 获取锁的调用过程
- 公平锁总结
- 释放锁的调用过程
- 非公平锁(NonfairSync)
- 获取锁调用过程
- 公平锁和非公平锁差别
1. AQS简介
ReentrantLock内部主要依赖于java同步框架AbstractQuerySynchronizer,类为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件,等等)提供一个框架,主要就是:维护锁的当前状态和线程等待列表
1.1 AQS同步队列结构
1.2 AQS获取同步状态的流程图
2. ReentrantLock
2.1 ReentrantLock类图
ReentrantLock主要基于AQS实现,有公平锁和非公平锁两种。
2.2 ReentrantLock运用demo
public class ReentrantLockExample {
int a= 0;
//默认是非公平锁
ReentrantLock lock = new ReentrantLock();
public void writer(){
//获取锁
lock.lock();
a++;
//释放锁
lock.unlock();
}
public void reader(){
//获取锁
lock.lock();
int i = a;
//释放锁
lock.unlock();
}
}
3. 公平锁(FairSync)
3.1 获取锁的调用过程
第一步: ReentrantLock.lock()
public void lock() {
sync.lock();
}
第二步: FairSync.lock()
final void lock() {
acquire(1);
}
第三步:AbstractQueuedSynchronizer.acquire(int arg)
public final void acquire(int arg) {
// 尝试获取锁,如果获取锁失败,那么将当前线程执行信息,放入到AQS的内部队列中
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 中断当前线程
selfInterrupt();
}
第四步: FairySync.tryAcquire(int acquires)
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取变量state, 这个变量非常重要,全程主要是围绕这个变量来做事
int c = getState();
// 当变量state==0 的时候,表示当前锁是空闲的,可以获取
if (c == 0) {
// 判断当前线程是不是等待最久的线程,就是说判断当前线程是不是在等待队列的第二个元素
// 因为队列head是当前(之前)拥有锁的线程
// 如果是,则表示等待时间最久 , 返回false , 表示没有线程比它等的更久
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 设置当前执行线程(一个内部变量,为了后面的可重入功能)为 “当前线程”
setExclusiveOwnerThread(current);
return true;
}
}
// state !=0 , 并且当前线程==当前执行器线程(一个内部变量)
else if (current == getExclusiveOwnerThread()) {
// 对state赋值, 由此可以看出,ReentrantLock是一个可重入锁,
// 但是有一点,就是重入多少次,就必须要unlock多少次,以保证最终state==0
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
公平锁,确保等待最久的线程优先获取锁
public final boolean hasQueuedPredecessors() {
Node t = tail; // 尾部元素
Node h = head; // 头部元素
// 队列为空的情况下也是返回false
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
3.2 公平锁总结
锁的竞争主要是获取state的状态,0表示没有线程占有他,大于0表示已经被占用了。 公平锁的实现在于判断当前线程是否是head的下一个元素,如果是,那么就OK,就是你了。 因为head一般是上一个占有锁的线程所有
3.3 释放锁的调用过程
第一步:ReentrantLock.unlock()
public void unlock() {
sync.release(1);
}
第二步:AbstractQueuedSynchronizer.release(int arg)
public final boolean release(int arg) {
// 修改state值,减一
if (tryRelease(arg)) {
Node h = head;
// 获取他头结点信息 (头部结点,就是当前拥有锁的线程所拥有)
if (h != null && h.waitStatus != 0)
// 主要作用是唤醒头结点的下一个结点
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 获取它的next结点
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
// 唤醒
LockSupport.unpark(s.thread);
}
第三步:Sync.tryRelease(int releases)
protected final boolean tryRelease(int releases) {
// 减法
int c = getState() - releases;
// 判断是否是执行线程去调用的unLock()
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// 当state=0,释放成功(锁已经空闲)
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
4. 非公平锁(NonfairSync)
4.1 获取锁调用过程
第一步:ReentrantLock:lock() 和公平锁一样
第二步:NonfairSync:lock()
final void lock() {
// 当线程进来之后,会直接判断当前state的值。如果是0 ,他能够直接通过
// cas操作,设置state的值为1 的话,那么竞争锁成功
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 上面设置失败的话,那么直接调用acquire方法
acquire(1);
}
第三步:AbstractQueuedSynchronizer:compareAndSetState(int expect,int update)
如果资源已经被占用
第四步:AbstractQueuedSynchronizer.acquire(int arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
第五步:NonfairSync.tryAcquire(int acquires)
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
第六步:Sync.nonfairTryAcquire(int acquires)
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;
}
5. 公平锁和非公平锁差别
获取锁的时候相差AbstractQueuedSynchronizer.hasQueuedPredecessors()方法的判断,是否需要取等待时间最长的那个队列,释放锁的流程都相同
参考文档:https://blog.csdn.net/u012394095/article/details/80518594