看本文章前,希望先看以下相关文章:
Lock lock = new ReentrantLock();
try{
lock.lock();
//临界区
}finally {
lock.unlock();
}
public interface Lock {
//加锁
void lock();
//获取锁可响应中断(相比较synchronized的优点):
void lockInterruptibly() throws InterruptedException;
//尝试非阻塞获取锁,调用该方法立即返回结果,而不是一直等待(阻塞)
boolean tryLock();
//尝试非阻塞获取锁,在规定时间如果没有获取锁,就返回false,相反为true
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//解锁
void unlock();
//获取等待通知组件,该组件与当前的锁绑定,当前线程只有获得了锁才能调用该组件的await()方法、调用后释放锁
Condition newCondition();
}
lockInterruptibly()
synchronized在等待获取锁时是不可中的);tryLock()
synchronized会一直等待锁;Condition newCondition()
相比synchronized更加灵活 //查询当前线程保持此锁的次数。
public int getHoldCount() {
return sync.getHoldCount();
}
//返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
//查询此锁是否由被线程保持。
public boolean isLocked() {
return sync.isLocked();
}
//查询是否是公平锁
public final boolean isFair() {
return sync instanceof FairSync;
}
//返回目前拥有此锁的线程(独占锁,共享锁),如果此锁不被任何线程拥有,则返回 null。
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();
}
//查询是否有些线程正在等待与此锁有关的conditon条件。
public boolean hasWaiters(Condition condition) {
}
//返回等待与此锁相关的给定条件的线程估计数。
public int getWaitQueueLength(Condition condition) {
}
// 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。
protected Collection<Thread> getWaitingThreads(Condition condition) {
}
}
private volatile int state;
:
abstract static class Node { ...}
来完成线程获取锁的排队工作;ExclusiveNode、Node、SharedNode、ConditionObject、ConditionNode
抽象AQS类
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
private static final long serialVersionUID = 7373984972572414691L; 序列化版本号
protected AbstractQueuedSynchronizer() {
} 构造方法
// Node status bits, also used as argument and return values
static final int WAITING = 1; // 常量1,表示线程等待
static final int CANCELLED = 0x80000000; // 常量负数,表示取消等待
static final int COND = 2; // 和等待队列有关
* +------+ prev +-------+ +------+ 通过Node实现了同步队列
* | head | <---- | first | <---- | tail |
* +------+ +-------+ +------+
*
abstract static class Node {
volatile Node prev; 链表中的prev,volatile修饰
volatile Node next; 链表中的next,volatile修饰
Thread waiter; Node包装的线程
volatile int status; 原子位操作,相当于状态,volatile修饰
}
private transient volatile Node head; 同步队列的头结点,volatile修饰
private transient volatile Node tail; 同步队列的尾结点,volatile修饰
private volatile int state; 同步状态,state==1,放入同步队列中,volatile修饰
//其他代码...
static final class Node {
JDK9之前的吧,稍后看一下JDK15
static final Node SHARED = new Node(); 共享模式
static final Node EXCLUSIVE = null; 独占模式
static final int CANCELLED = 1; 标识线程已处于结束状态
// 值为1,在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,其结点的waitStatus为CANCELLED,
//即结束状态,进入该状态后的结点将不会再变化。
static final int SIGNAL = -1; 标识线程已处于可被唤醒状态
//值为-1,被标识为该等待唤醒状态的后继结点,当其前继结点(头节点)的线程释放了同步锁或被取消,将会通知该后继结点的线程执行。
//说白了,就是处于唤醒状态,只要前继结点释放锁,就会通知标识为SIGNAL状态的后继结点的线程执行。
static final int CONDITION = -2; 标识线程已处于条件状态
//值为-2,与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,
//当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。
static final int PROPAGATE = -3; 与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态
//0状态:值为0,代表初始化状态
volatile int waitStatus; 同步队列中结点状态:CANCELLED、SIGNAL、CONDITION、PROPAGATE 4种
volatile Node prev; 同步队列中前驱结点
volatile Node next; 同步队列中后继结点
volatile Thread thread; 请求锁的线程
Node nextWaiter; 等待队列中的后继结点,这个与Condition的等待队列有关
final boolean isShared() {
判断是否为共享模式
return nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {
获取前驱结点
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
//省略其他代码
}
Node中的status状态
static final int WAITING = 1; 常量1,表示线程等待
static final int CANCELLED = 0x80000000; 常量负数,表示取消等待
static final int COND = 2; 在等待队列中,下面具体分析
/** CLH Nodes */
abstract static class Node {
volatile Node prev; 同步队列中前驱结点
volatile Node next; 同步队列中后继结点
Thread waiter; 请求锁的线程
volatile int status; 同步队列中结点状态:WAITING、COND、CANCELLED 3种
以下方法底层都是CAS操作
final boolean casPrev(Node c, Node v) {
// for cleanQueue
return U.weakCompareAndSetReference(this, PREV, c, v);
}
final boolean casNext(Node c, Node v) {
// for cleanQueue
return U.weakCompareAndSetReference(this, NEXT, c, v);
}
final int getAndUnsetStatus(int v) {
// for signalling
return U.getAndBitwiseAndInt(this, STATUS, ~v);
}
final void setPrevRelaxed(Node p) {
// for off-queue assignment
U.putReference(this, PREV, p);
}
final void setStatusRelaxed(int s) {
// for off-queue assignment
U.putInt(this, STATUS, s);
}
final void clearStatus() {
// for reducing unneeded signals
U.putIntOpaque(this, STATUS, 0);
}
private static final long STATUS
= U.objectFieldOffset(Node.class, "status");
private static final long NEXT
= U.objectFieldOffset(Node.class, "next");
private static final long PREV
= U.objectFieldOffset(Node.class, "prev");
}
相比于早期的JDK,JDK15将Node的静态类有好几种
static final class ExclusiveNode extends Node {
} 继承Node类的独占模式下的Node类
static final class SharedNode extends Node {
} 继承Node类的共享模式下的Node类
static final class ConditionNode extends Node implements ForkJoinPool.ManagedBlocker {
ConditionNode nextWaiter; 与等待队列中的后继结点,这个与Condition的等待队列有关
}
共享模式(如Semaphore);
独占模式(如ReetrantLock);
无论是共享模式还是独占模式的实现类,其内部都是基于AQS实现的,也都维持着一个虚拟的同步队列,当没有请求到锁时,需将其加入同步队列等待获取锁,而这系列操作都由AQS协助完成,这也是作为基础组件的原因;
无论是Semaphore还是ReetrantLock,其内部绝大多数方法都是间接调用AQS完成的。
通过图我们知道AQS下有几种:
以ReentrantLock为例,简单讲解ReentrantLock(独占锁)与AQS的关系 :
ReentrantLock内部存在3个实现类:
AQS的深入理解:(总结自别人的文章,大佬!!!)
AQS提供给独占模式和共享模式的模板方法如下:
AQS中提供的主要模板方法,由子类实现❤
protected boolean tryAcquire(int arg) {
独占模式下获取锁的方法❤
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
独占模式下解锁的方法❤
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
共享模式下获取锁的方法❤
throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
共享模式下解锁的方法❤
throw new UnsupportedOperationException();
}
protected boolean isHeldExclusively() {
判断是否为持有独占锁❤
throw new UnsupportedOperationException();
}
public ReentrantLock() {
重入锁对的空构造器,直接就是通过非公平锁来进行获取锁等操作
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
重入锁的有参构造器构造器,fair-->非公平锁 true-->公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
final void lock() {
加锁操作,虽然lock()不是抽象类,但是initialTryLock()是抽象类,
if (!initialTryLock()) 具体由子类也就是公平/非公平锁实现;
acquire(1); acquire(1)再次请求同步状态
}
initialTryLock()
和acquire(1)
(其实核心是里面的tryAcquire(int acquires)
)方法,这两个方法体现了公平/非公平锁加锁的不同。我们通过下图先简单看一下:hasQueuedPredecessors()
这个方法 @ReservedStackAccess
final void lock() {
if (!initialTryLock())
acquire(1);
}
static final class NonfairSync extends Sync {
非公平锁类中有两个方法(如下):
private static final long serialVersionUID = 7316153563782823691L;
final boolean initialTryLock() {
方法1:尝试获取锁,也就是获取同步状态state
Thread current = Thread.currentThread(); 具体操作如下:
//当前线程获得了锁,CAS比较并设置state的值,设置成功,则通过AOS(AQS继承类)的方法来设置当前线程持有独占锁,并返回
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
//如果CAS比较设置失败,则看当前线程是否已经获得锁,在此加锁,重入锁
} else if (getExclusiveOwnerThread() == current) {
int c = getState() + 1;
if (c < 0) // overflow c<0说明不会持有锁,发生错误,具体什么原因可能造成这样不清楚……希望大佬解答
throw new Error("Maximum lock count exceeded");
//设置当前同步状态,当前只有一个线程持有锁,因为不会发生线程安全问题,可以直接执行 setState(c);
setState(c);
return true;
} else
return false;
}
/**
* Acquire for non-reentrant cases after initialTryLock prescreen
*/
protected final boolean tryAcquire(int acquires) {
//方法2:尝试获得同步状态(锁)
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread()); //成功则将独占锁线程设置为当前线程
return true;
}
return false;
}
}
public final void acquire(int arg) {
if (!tryAcquire(arg))
acquire(null, arg, false, false, false, 0L);
}
final boolean initialTryLock()
没有请求到同步状态,再通过tryAcquire(int acquires)
也没有请求到同步状态,这时候才执行的acquire(null, arg, false, false, false, 0L)
。initialTryLock()
更突出的是一个重入锁的功能,当没有获取到锁,也就压根不可能有重入的机会,所以调用tryAcquire(int acquires)
就可以了。 / **
* Main acquire method, invoked by all exported acquire methods. 主要的获取方法,由所有导出的获取方法调用
*
* @param node //除非是重新获取condition条件,否则为空
* @param arg //加锁次数
* @param shared //控制是否时共享线程队列也就是SharedNode的布尔值
* @param interruptible //是否时可中断线程
* @param timed //是否由最长等待时间
* @param time //中断超时时间
* //获得state时为正值,超时时为0,中断时为负值
* @return positive if acquired, 0 if timed out, negative if interrupted
*/
final int acquire(Node node, int arg, boolean shared, boolean interruptible, boolean timed, long time) {
// 带入参数:null, arg, false, false, false, 0L 独占锁,不可中断,不会超时
Thread current = Thread.currentThread(); // 获取当前线程
byte spins = 0, postSpins = 0; // 自旋变量和之前的自旋变量postSpins
boolean interrupted = false, first = false; // 中断变量值interrupted,first表示第一次进入方法
Node pred = null; // 存储前置节点
// 自旋获取锁
for (;;) {
// 循环的第一次判断为 true && false (node为null pred=null,null!=null。返回false就不执行后面的赋值和判断)
// 自旋后的第二次由于是刚创建的则prev为null因此还是false,pred=null,还是false不执行
// 当node!=null且node.prev!=null说明节点已经入队了,因此第二个判断返回true需要判断!(first = (head == pred))返回的是false
if (!first && (pred = (node == null) ? null : node.prev) != null &&!(first = (head == pred))) {
// 进入这个代码块说明队列不为空且不止一个线程在等待
if (pred.status < 0) {
// 实际就是传入的node的前一个节点的status是否<0
cleanQueue(); // 如果<0,则清空队列
continue;
} else if (pred.prev == null) {
Thread.onSpinWait(); // 自旋等待确保序列化
continue;
}
}
//循环第一次 false || true,需要进入代码块。pred在第一次判断就被赋值为null,循环进入前也是null
if (first || pred == null) {
boolean acquired;
try {
if (shared)
acquired = (tryAcquireShared(arg) >= 0);// 这个方法由Semaphore调用,意思判断剩余信号量是否>=0
else
/**
* 尝试抢占锁,如果没有抢到则会自旋,tryAcquire(arg);会一直重复调用,直到抢占成功
* 子类实现的方法。例如非公平锁的tryAcquired(1);
* 如果state为0,则acquired则会变成true。将当前线程设置为独占并更新state为arg
* 如果state不为0,则acquired=false,说明被其它线程上锁了
*
* 自旋的起点是后面
* if (node == null) {
* if (shared)// 如果是共享队列节点则创建SharedNode,然后由于后面没有代码则会自旋for循环重新执行一遍只是这个时候node不为null
* node = new SharedNode(); //这个是线程队列的头节点,用来标识这个队列是一个共享锁线程等待队列
* else // 如果是一个排他锁创建一个排他节点
* node = new ExclusiveNode();// 线程队列的头节点,标识是一个排他锁线程队列
* }
* 然后到这里的tryAcquire(arg);一直原地踏步,直到抢占到锁
* acquired更新为true
* 进入catch后面的if
*
*/
// 会回到子类(NonfairSync、FairSync等)的实现的方法尝试获得锁,如果失败则还是false,直到成功true
acquired = tryAcquire(arg);
} catch (Throwable ex) {
cancelAcquire(node, interrupted, false);
throw ex;
}
// true说明当前线程抢占到锁了
if (acquired) {
//信号量的使用一个信号量,如果成功也会进行判断,使用信号量成功则进入,没有成功说明信号量使用完了
if (first) {
if (first) {
node.prev = null;
head = node;
pred.next = null;
node.waiter = null;
if (shared)
signalNextIfShared(node);
if (interrupted)
current.interrupt();
}
return 1;// 返回1标签抢占到锁了这是这个方法的唯一结束点,其它分支始终都会死循环
}
}
/**
*
* AQS队列的形成起点,头节点就是下面的SharedNode或ExclusiveNode,然后自旋
*
*/
// 第一次进入node传入的是null因此进入
// 第二次由于第一次进入后node=new SharedNode();或node = new ExclusiveNode();则这个if就不会在进入,会进入第二个if因为pred=null
if (node == null) {
// 形成头节点
if (shared) // 如果是共享队列节点则创建SharedNode,然后由于后面没有代码则会自旋for循环重新执行一遍只是这个时候node不为null
node = new SharedNode(); //这个是线程队列的头节点,用来标识这个队列是一个共享锁线程等待队列
else // 如果是一个排他锁创建一个排他节点
node = new ExclusiveNode();//线程队列的头节点,标识是一个排他锁线程队列
} else if (pred == null) {
//try to enqueue 尝试进入同步队
node.waiter = current; 将当前线程放到node中,也就是当前结点中(如果不进队,都不用放入到当前结点)中)
Node t = tail; 判断尾部结点
node.setPrevRelaxed(t); avoid unnecessary fence,应该是一种性能的优化,Native操作,❔❔❔
if (t == null) //如果当前尾部结点为空,说明还没有同步队列,需要创建一个同步队列
tryInitializeHead(); //tryInitializeHead()就是用来创建同步队列的
else if (!casTail(t, node)) //如果尾部节点不为空,则将尾部结点替换成node
node.setPrevRelaxed(null); back out 如果替换不成功,按以前的JDK是要循环,再次尝试,但这里是
else 退出了?感觉不像❔❔❔
t.next = node; //casTail(t, node)替换成功,因为双向链表,所以要指向尾部结点
//到这里就进入同步队列了
} else if (first && spins != 0) {
--spins; // reduce unfairness on rewaits 自旋操作
Thread.onSpinWait();
} else if (node.status == 0) {
node.status = WAITING; // enable signal and recheck
} else {
long nanos;
spins = postSpins = (byte)((postSpins << 1) | 1);
if (!timed)
LockSupport.park(this);
else if ((nanos = time - System.nanoTime()) > 0L)
LockSupport.parkNanos(this, nanos);
else
break;
node.clearStatus();
if ((interrupted |= Thread.interrupted()) && interruptible)
break;
}
}
return cancelAcquire(node, interrupted, interruptible); 最终都没能获取同步状态,结束该线程的请求❤
}
public final void acquire(int arg) {
//JDK15的acquire
if (!tryAcquire(arg))
acquire(null, arg, false, false, false, 0L);
}
public final void acquire(int arg) {
//JDK8的acquire
//再次尝试获取同步状态
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //Node.EXCLUSIVE对应的独占锁
selfInterrupt();
}
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
的效果 其实就是上面JDK15的acquire(Node node, int arg, boolean shared, boolean interruptible, boolean timed, long time)
的效果:我们按JDK8(别问为什么!问就是菜!)来分析:private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode); //将请求同步状态失败的线程封装成结点
Node pred = tail;
//如果是第一个结加入肯定为空,尾部结点一定为null,直接进入队列即可。
if (pred != null) {
//如果非第一个结点则直接执行CAS入队操作,尝试添加到尾部
//同步队列中双向链表的尾部的具体操作:
node.prev = pred;
if (compareAndSetTail(pred, node)) {
//使用CAS执行尾部结点替换,尝试在尾部快速添加,如果CAS失败,执行enq
pred.next = node; //为什么CAS会失败呢?因为这时候可能有多个线程包装成的结点放入到队尾
return node;
}
}
/*
入队的两种情况:
1、如果是第一个结点则直接加入,也就是执行enq入队操作,顺便在enq方法中创建同步队列
2、如果不是第一个结点,则说明队列存在,但是在上面的CAS操作没有成功替换尾结点,则执行enq入队操作
*/
enq(node);
return node; //返回同步队列尾部结点
}
private Node enq(final Node node) {
for (;;) {
//死循环,return跳出
Node t = tail;
//对应入队的第一种情况:初始化 如果尾部结点为null,即没有头节点和尾结点,也就是没同步队列,需要创建头节点
if (t == null) {
if (compareAndSetHead(new Node())) //创建并使用CAS设置头结点
tail = head;
//对应入队的第二种情况:通过CAS在队尾添加新结点,并设置新的队尾
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t; //这里的返回没有意义!!只是为了将结点添加到尾部后(维持这个同步队列),退出这个死循环❤
}
}
}
}
addWaiter
方法的作用有两个:
public final void acquire(int arg) {
//再次尝试获取同步状态
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter(Node.EXCLUSIVE), arg)
,也就是添加到同步队列后,将要进行一个自旋过程,就是说每个结点都在观察时机是否成熟(也就是说是否可以获取同步状态的时候),我们从同步队列退出并结束自旋;acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
/*
@node:addWaiter(Node mode)方法返回的尾部结点
@arg:一般为1,代表state状态,同意去抢夺锁
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//自旋,死过程
final Node p = node.predecessor(); //获取前驱结点p
//当且仅当前驱节点为头结点head才尝试获取同步状态,然后来判断头节点是否释放同步状态
if (p == head && tryAcquire(arg)) {
setHead(node); //当获取了同步状态state==1,也就是头节点已经释放了同步状态,才可将node设置为头结点
//设置完头节点后的操作:3步
p.next = null; //1、清空原来头结点的引用(next)便于GC
failed = false; //2、说明已经获得了同步状态,不需要自旋
return interrupted; //3、返回中断标志,也就是退出自旋
}
/*
判断挂起的两种情况:
1、前驱结点不是head;
2、前驱节点是头节点head但是它还没释放同步状态,也就是当前节点获取同步状态失败,则判断是否挂起线程;
注意:大部分结点执行这个方法其实首先都是先挂起,只有前结点为头节点才有申请同步状态的相关操作
*/
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node); //最终都没能获取同步状态,结束该线程的请求
}
}
acquireQueued(final Node node, int arg)
方法的总结:
acquireQueued(final Node node, int arg)
这个方法的其他方法:setHead()
方法://设置为头结点
private void setHead(Node node) {
head = node;
//清空结点数据
node.thread = null;
node.prev = null;
}
setHead(Node node)
方法分析:
从图可知:
接下来我们看一下是否挂起的具体操作:
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//pred是node的前驱节点,但是pred不是头节点,因为如果是头节点,node就不用挂起了:
int ws = pred.waitStatus; //获取前驱节点的等待状态
if (ws == Node.SIGNAL) //如果为等待唤醒(SIGNAL)状态则返回true
return true;
if (ws > 0) {
//如果ws>0 则说明是结束状态,遍历前驱结点直到找到不是结束状态的结点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果ws小于0又不是SIGNAL状态,一般是从等待队列中刚转过来,是Condition状态 则将其设置为SIGNAL状态,代表该结点的线程正在等待唤醒。
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
//只有当前驱节点是唤醒状态,才挂起当前结点
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); //将当前线程挂起
//获取线程中断状态,interrupted()是判断当前中断状态,
//并非中断线程,因此可能true也可能false,返回
return Thread.interrupted();
}
分析:
shouldParkAfterFailedAcquire()
方法的作用(三种情况的判断):
parkAndCheckInterrupt()
方法的作用:
注意:park()和unpark()方法都是unsafe类的native方法。
以上基于ReentrantLock类的加锁方式就都结束了
lockInterruptibly()
或者tryLock()
方法,最终它们都间接调用到doAcquireInterruptibly()
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // 帮助GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException(); 直接抛异常,中断线程的同步状态请求,最大的不同
}
} finally {
if (failed)
cancelAcquire(node);
}
}
ReentrantLock
的NonfairSync
加锁操作,现在我们看一下基于 ReetrantLock的 FairSync
加锁操作; /*
Sync类的lock()方法:通过initialTryLock()以及acquire(1)中的tryAcquire(int acquires)由具体
实现类(公平非公平锁)的实现来达到不同的效果
*/
@ReservedStackAccess
final void lock() {
if (!initialTryLock())
acquire(1);
}
abstract boolean initialTryLock();
// FairSync的tryAcquire(int acquires)方法
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && !hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// NonfairSync的tryAcquire(int acquires)方法
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//FairSync的initialTryLock()方法
final boolean initialTryLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) {
if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
}
return false;
}
// NonfairSync的initialTryLock()方法
final boolean initialTryLock() {
Thread current = Thread.currentThread();
if (compareAndSetState(0, 1)) {
// first attempt is unguarded
setExclusiveOwnerThread(current);
return true;
} else if (getExclusiveOwnerThread() == current) {
int c = getState() + 1;
if (c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
} else
return false;
}
FairSync
和NonfairSync
类的两个具体实现的方法的源码对比:
FairSync
的 tryAcquire(int acquires)
和 initialTryLock()
比NonfairSync
的这两个方法,前者在通过CAS方法尝试设置state值前,调用了AQS的hasQueuedThreads()
方法; public final boolean hasQueuedThreads() {
for (Node p = tail, h = head; p != h && p != null; p = p.prev)
//status<0的时候,就是结束状态(注意区分JDK15和JDK8的区别,这是JDK15的方法)
if (p.status >= 0)
return true;
return false;
}
hasQueuedThreads()
判断同步队列是否存在结点,如果存在必须先执行完同步队列中结点的线程; public void unlock() {
//ReentrantLock类的unlock方法
sync.release(1);
}
AQS的release()方法
public final boolean release(int arg) {
//sync.release()方法,是ReentrantLock内部类Sync的方法,然后Sync继承
//尝试释放锁
if (tryRelease(arg)) {
//于AQS,而Sync没有重写release()方法,因此这是AQS的实现方法
signalNext(head); //通过另一方面说明、AQS实现了最核心、最基本的方法、但是类似具体加锁
return true; //释放锁等就由相应的实例类来实现(底层细节还是AQS实现);
}
return false;
}
ReentrantLock类中的内部类Sync实现的tryRelease(int releases)----》尝试释放锁的操作
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = getState() - releases; 释放锁,一般getState()为1,releases为1
if (getExclusiveOwnerThread() != Thread.currentThread())
throw new IllegalMonitorStateException();
boolean free = (c == 0);
if (free) 判断状态是否为0,如果是则说明已经释放同步状态,可以释放锁了
// 先释放同步状态,再释放锁,虽然时间段但是也有时间差,可能造成同步队列中头节点的后驱结点获取同步状态state==1后
//,但是不能得到锁,上面有提到
setExclusiveOwnerThread(null); 设置Owner为null,也就是释放锁了(不让线程持有锁了)
setState(c); 设置更新同步状态
return free;
}
//释放同步状态的操作相对简单些,tryRelease(int releases)方法是ReentrantLock类中内部类自己实现的,
//因为AQS对于释放锁并没有提供具体实现,必须由子类自己实现。
//释放同步状态后会使用 signalNext(head) 唤醒后继结点的线程,因此我们看一下这个方法
AQS的signalNext(Node h)方法---》释放锁后唤醒后继结点的线程的操作:
private static void signalNext(Node h) {
当头节点不为空,且后驱结点不为空,将其状态设置成WAITING
Node s; 然后唤醒(unpark())最前边未放弃的线程;
if (h != null && (s = h.next) != null && s.status != 0) {
s.getAndUnsetStatus(WAITING);
LockSupport.unpark(s.waiter);
}
}
LockjSupport的unpark(Thread thread)方法 ---》唤醒同步队列中最前边未放弃的线程(也就是状态不为CANCELLED的线程结点s)
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
}
总体来看,重入锁ReentrantLock,是一个基于AQS并发框架的并发控制类,其内部实现了3个类:
内部类ConditionObject
来实现了这个Condition接口
。public interface Condition {
/**
* 当前线程进入等待状态直到被通知(signal)或中断
* 当其他线程调用singal()或singalAll()方法时,该线程将被唤醒
* 当其他线程调用interrupt()方法中断当前线程
* await()相当于synchronized等待唤醒机制中的wait()方法
*/
void await() throws InterruptedException;
//当前线程进入等待状态,直到被唤醒,该方法不响应中断
void awaitUninterruptibly();
//调用该方法,当前线程进入等待状态,直到被唤醒或被中断或超时;其中nanosTimeout指的等待超时时间,单位纳秒
long awaitNanos(long nanosTimeout) throws InterruptedException;
//同awaitNanos,但可以指明时间单位
boolean await(long time, TimeUnit unit) throws InterruptedException;
//调用该方法当前线程进入等待状态,直到被唤醒、中断或到达某个时间期限(deadline),
//如果没到指定时间就被唤醒,返回true,其他情况返回false
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获取与Condition相关联的锁,功能与notify()相同
void signal();
//唤醒所有等待在Condition上的线程,该线程从等待方法返回前必须获取与Condition相关联的锁,功能与notifyAll()相同
void signalAll();
}
ConditionObject:
中的属性: public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
//等待队列的第一个等待结点
private transient ConditionNode firstWaiter;
/** Last node of condition queue. */
//等待队列的最后一个等待结点
private transient ConditionNode lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() {
} //空构造器
//.....其他代码
ConditionNode nextWaiter; // link to next waiting node 说明是单向链表
await()
方法的线程是如何加入等待队列的,而又是如何从等待队列中被唤醒的;(JDK15的分析不懂,看JDK8的吧) public final void await() throws InterruptedException {
if (Thread.interrupted()) //判断持有锁的线程是否被中断
throw new InterruptedException(); //没有中断,继续往下执行
ConditionNode node = new ConditionNode(); //接下来准备创建新结点加入等待队列并返回
long savedState = enableWait(node); //释放当前线程锁即释放同步状态
LockSupport.setCurrentBlocker(this); // for back-compatibility
boolean interrupted = false, cancelled = false;
while (!canReacquire(node)) {
//判断结点是否在同步队列(SyncQueue)中,即是否被唤醒
if (interrupted |= Thread.interrupted()) {
if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
break; // else interrupted after signal
} else if ((node.status & COND) != 0) {
try {
ForkJoinPool.managedBlock(node);
} catch (InterruptedException ie) {
interrupted = true;
}
} else
Thread.onSpinWait(); // awoke while enqueuing
}
LockSupport.setCurrentBlocker(null);
node.clearStatus();
acquire(node, savedState, false, false, false, 0L);
if (interrupted) {
if (cancelled) {
unlinkCancelledWaiters(node);
throw new InterruptedException();
}
Thread.currentThread().interrupt();
}
}
public final void await() throws InterruptedException {
//首先判断线程是否被中断,中断则直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//创建新结点加入等待队列
Node node = addConditionWaiter();
//释放当前结点的锁即释放同步状态
int savedState = fullyRelease(node);
int interruptMode = 0;
//判断结点是否在同步队列(SyncQueue)中,即是否被唤醒
while (!isOnSyncQueue(node)) {
//如果不在同步队列中,则挂起线程(在等待队列中挂起)
LockSupport.park(this);
//判断是否被中断唤醒,如果是退出循环。
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//被唤醒后执行(因此唤醒后会由等待队列转移到同步队列)自旋操作争取获得锁,同时判断线程是否被中断
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// clean up if cancelled
if (node.nextWaiter != null)
//清理等待队列中不为CONDITION状态的结点
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
// 执行addConditionWaiter() , 添加到等待队列。
private Node addConditionWaiter() {
Node t = lastWaiter; // t是等待队列的尾节点
// 判断是否为结束状态的结点并移除,也就是将t设置为非结束状态的尾部节点
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//创建新结点状态,并设置为CONDITION状态
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//加入等待队列,如果等待队列为空,则将当前节点设为第一个节点,否则作为新的尾节点
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node; //返回等待队列尾部节点(前一个结点不是结束状态)
}
addConditionWaiter()
方法:将当前线程封装成node结点加入到等待队列;fullyRelease(node)
方法:释放同步状态并唤醒后继(同步队列的)结点的线程;isOnSyncQueue(node)
方法:判断结点是否在同步队列中,注意是个while循环;
acquireQueued(node, savedState)
执行自旋操作争取锁(当前线程结点从等待队列转移到同步队列并开始努力获取锁)。
public final void signal() {
ConditionNode first = firstWaiter; //获取等待队列的第一个结点
if (!isHeldExclusively()) //判断是否持有独占锁,如果不是抛出异常
throw new IllegalMonitorStateException();
if (first != null) //唤醒等待队列第一个结点的线程
doSignal(first, false);
}
private void doSignal(ConditionNode first, boolean all) {
//all传入的false,用来判断是不是signalAll
while (first != null) {
ConditionNode next = first.nextWaiter;
//移除条件等待队列中的第一个结点如果后继结点为null,那么说明没有其他结点,因此将尾结点也设置为null
if ((firstWaiter = next) == null)
lastWaiter = null;
if ((first.getAndUnsetStatus(COND) & COND) != 0) {
//判断该节点是不是条件状态,是则进入同步入队
enqueue(first);
if (!all) //看用不用循环判断
break;
}
first = next; //循环找一个
}
}
final int getAndUnsetStatus(int v) {
// for signalling //这里我不太会分析,但是确实比JDK8要优化了,底层用的
return U.getAndBitwiseAndInt(this, STATUS, ~v); //Unsafe类
}
@ForceInline
public final int getAndBitwiseAndInt(Object o, long offset, int mask) {
int current;
do {
current = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset,
current, current & mask));
return current;
}
doSignal(first, false)
doSignal(ConditionNode first, boolean all)
方法显示了在JDK8上的 doSignal(first)的优化:
signal()
还是signalAll()
;