这个就是AQS。
关键词:先进先出的等待队列。int值状态。
设计模式:模板方法。
总结:
XXXState(int):以原子的方法来操作int值,获得同步器。
内部没有同步的接口,而是定义了acquireInterruptinly(int)方法。
支持独占和共享模式,或者两者同时支持。
上面的知识点:
1.isHeldExclusively:是不是共享的
2.只有上面的五个方法是可以重写的,其他的都是final的。
网址:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
看ReentrantLock的源码,我们关注的就是lock和unlock:
看lock方法,有公平和非公平的概念。
委托给了同步器。
同步器的继承关系,是AQS的实现。
同步器里面有lock方法。但是是抽象的。同步器有公平和非公平的同步器实现,重写这个方法。
有公平和非公平额概念。
lock-tryAcquare。
看公平的同步器lock方法
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
看这个方法acquire(1),其实就是AQS的方法;
public final void acquire(int arg) {
if (!tryAcquire(arg) &&//tryAcquire相当于重入锁,重入失败的话就走下一个方法&&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
几个方法的汇总解释:https://blog.csdn.net/weixin_38003389/article/details/85935982
看这个方法AQS的tryAcquire,其实已经被sync同步器重写了。
点进去
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
不看这个方法,看这个
24:33
小总结:rencentLock关注的是lock和unlock,是委托给同步器的,同步器继承AQS,分为公平的同步器和非公平的同步器。
同步器调用lock方法,lock调用acquire(其实是AQS的acquire方法),调用tryAcquire(时间上是recentLock的公平或者非公平的sync的)。
进入tryAcquire方法----作用就是竞争资源:
acquire调用tryAcquire公平锁。
同步器是AQS的实现。
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;
}
注意AQS里面有一个Node,这个是获得状态的。getState。
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
..............
这个node就是用来实现FIFO队列的。
主要看这个属性:
首先找个比较简单的看下,非公平的比较简单:
getState中的state是voliate的
final boolean nonfairTryAcquire(int acquires) {//主要是根据线程修改状态的
final Thread current = Thread.currentThread();//拿到当前的线程获取状态
int c = getState();//获取线程的状态
if (c == 0) {//是0就是第一次进入
if (compareAndSetState(0, acquires)) {//注意这里传的是1 是0就把状态设置为1
setExclusiveOwnerThread(current);//把当前的线程设置为独占的线程
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//不为0就不是初始值和进出计数器原理是一样的。判读是不是当前的线程。
int nextc = c + acquires;//重入
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);//更新状态,相当于lockCount++
return true;
}
return false;
}
注意compareAndSetState方法:https://blog.csdn.net/walleit/article/details/88542881
cas需要输入两个参数一个是期望的值一个是新值。期望的值是要和我们的当前对象的值比较的。
类似于这个代码:
看实现非公平的:
找个是把0扔进去,状态设置为1。
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
再回来看acquire方法:
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 当前线程和无锁时候是真否则是假,执行下后面的方法
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里成功就是可以拿到锁,否则就是false。
这个方法:
addWaiter(Node.EXCLUSIVE)//这个传的是null,不是独占的
其他的线程来的时候不能执行,会放在等待队列里面。
private Node addWaiter(Node mode) {//这个node就是我们说的队列的数据结构
Node node = new Node(Thread.currentThread(), mode);//创建node
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;//找到等待队列的尾部节点
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {//设置当前的节点是尾节点
pred.next = node;
return node;
}
}
enq(node);//节点是null走这个方法
return node;
}
上面的方法就是加入链表的尾部。
这个其实使个双向链表。
尾部节点是空的就执行enq方法,新建链表:
private Node enq(final Node node) {//传进来的是新追加的节点,进入这个方法是因为其前一个节点是null
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))//没有节点就先创建一个节点,并设置为头节点
tail = head;
} else {
node.prev = t;//这存在就执行上一步操作添加
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
再往下,我们要把要等待的线程阻塞掉。传进去的node就是新创建的node。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//arg是1
selfInterrupt();
}
final boolean acquireQueued(final Node node, int arg) {//传进来的node是新创建的node
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//获取其前一个节点
if (p == head && tryAcquire(arg)) {//前一个节点是头节点就干掉头节点执行tryAcquire
setHead(node);// 设置当前的节点是头节点
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&//前一个节点不是头节点,就把前一个节点和当前节点传入方法,进入这个方法
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这个也是非常重要的方法。
我们虽然创建了队列但是要等待的线程还是没有被阻塞掉。这个是一直判断的直到判断成功。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)//获得前一个节点的等待状态。
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {//多个lock方法锁住了
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;//移除处于cancel的节点,因为其不需要竞争cpu的资源
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
接着再往下走,这个方法
parkAndCheckInterrupt()
这个方法其实就是让当前的线程等待,等待唤醒。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//arg是1
selfInterrupt();
}
--------
解锁:unlock
看RecentLock的unlock
还是调用的同步器的release方法,传的参数是1
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(arg)
还是需要我们重写的。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())//当前线程不是进来的线程就抛出异常
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//所有的lock调用unlock值才为0
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
看下总的release方法。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//叫醒其他线程抢占cpu资源相当于notify
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;//拿到node的等待状态
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);//比较并设置状态的值,等于0则归为0
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);//叫醒其他的线程争夺cpu的资源
}
----------------------------
总结:AQS维护队列和线程的状态,自己写同步器继承AQS重写它的方法。