ReentrantLock:
1.可重入
2.公平锁或者非公平锁
3.手动加锁,手动释放
AQS:ReentrantLock内部维护了一个AQS队列,AQS的本质是一个双向链表。AQS中存放着排队等待锁的线程对象(Node)
ReentrantLock公平锁的加锁过程
public final void acquire(int arg) {
//tryAcquire(arg):尝试当前线程加锁
//acquireQueued(addWaiter(Node.EXCLUSIVE), arg)):将当前线程加入到AQS队列
//selfInterrupt():重置线程interrupted状态
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();//线程被唤醒后,还原interrupt.因为Thread.interrupted()可能会重置interrupt
}
tryAcquire(arg) :尝试获取锁,如果能加锁成功,返回true,加锁失败返回false
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取当前锁的状态(state)
/*0:没有线程获取锁或者获取到锁的对象已经释放释放锁
/*1:表示当前锁已经被线程持有了
/*大于1:表示当前锁被线程重入了
int c = getState();
if (c == 0) {//表示当前锁没有线程得到或者得到当前锁的线程已经释放锁
//hasQueuedPredecessors:查看当前AQS队列是否有线程在排队
//第一个线程加锁时,hasQueuedPredecessors()返回false,继续执行CAS给当前线程加锁
//CAS成功之后执行setExclusiveOwnerThread(),将当前线程设置为持有锁的线程,当前线程加锁成功
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//c!=0:当前lock已经被线程持有,如果当前线程是持有当前锁的线程,state+1(表示当前锁的重入次数+1),当前线程加锁成功
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//c!=0 && exclusiveOwnerThread != currentThread:lock已经被其他线程持有,当前线程加锁失败
return false;
}
hasQueuedPredecessors():查看AQS队列是否有线程在排队等待唤醒
public final boolean hasQueuedPredecessors() {
Node h, s;
//head:AQS队列的头节点,如果AQS队列没有被初始化的时候,head==null
// 如果AQS队列已经被初始化了,head指向AQS的第一个Node节点
if ((h = head) != null) {//第一个线程访问的时候,AQS队列没有初始化,head==null
if ((s = h.next) == null || s.waitStatus > 0) {
s = null; // traverse in case of concurrent cancellation
for (Node p = tail; p != h && p != null; p = p.prev) {
if (p.waitStatus <= 0)
s = p;
}
}
if (s != null && s.thread != Thread.currentThread())
return true;
}
return false;
}
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)):将当前线程加入到AQS队列
Node节点的构造
//Node的主要参数(从中可以看出AQS是一个双向链表)
Node{
Node pre;//指向当前节点的前一个节点
Node next;//当前节点的下一个节点
Thread thread;//当前节点绑定的线程对象
volitale int waitStatus;//当前node节点的状态(0:初始状态 -1:可以被唤醒 1:取消 -2:condition -3:progate)
}
1.addWaiter(Node.EXCLUSIVE):初始化AQS队列或者直接将当下线程的node节点加到队尾tail(AQS队列的队尾)
private Node addWaiter(Node mode) {
Node node = new Node(mode);//初始化一个node节点 node{pre=null,next=null,waitStatus=0,thread:this(当前线程)}
//如果AQS队列已经被初始化过了,就直接将当前线程节点设置为tail
//如果AQS队列还没有被初始化,首先执行initializeSyncQueue()初始化AQS队列,然后再将当前node设置为tail
for (;;) {//自旋,相当于while(true)
//tail:AQS队列的队尾。 AQS队列没有初始化的时候,tail=null
Node oldTail = tail;
if (oldTail != null) {//将当前的node节点设置为新的tail节点
node.setPrevRelaxed(oldTail);//将node节点的上一个节点设置为之前老的tail
if (compareAndSetTail(oldTail, node)) {//CAS将新的node设置为新的tail
oldTail.next = node;
return node;
}
} else {
//初始化AQS队列
initializeSyncQueue();
}
}
}
initializeSyncQueue():初始化AQS队列(AQS的精髓:haad节点的thread永远为null)
private final void initializeSyncQueue() {
Node h;
//new Node()构造了一个thread=null的空节点,此时head和tail都指向同一个节点
if (HEAD.compareAndSet(this, null, (h = new Node())))
tail = h;
}
2:acquireQueued(Node node, arg)):将node加入到AQS队列
//node:当前线程的节点 ,arg = 1(传进来的就是1)
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;//表示线程的打断状态
try {
//执行情况:1,线程加锁失败,加入到AQS,2.释放锁时,线程被唤醒,继续执行
for (;;) {
final Node p = node.predecessor();//当前节点的前一个节点
//前一个节点是头节点,说明了当前的线程节点是第二个,这个时候调用tryAcquire(arg)再次尝试加锁
//原因:可能当前线程在加入到AQS队列的时候,之前持有锁的线程释放了锁,这个时候当前线程就有机会直接获取锁
if (p == head && tryAcquire(arg)) {
//当前线程节点获取尝试获取到了锁,会将当前节点设置为新的head节点,
//并且将节点的thread设置为null,pre也设置为null,断开指向前一个节点的引用
setHead(node);
//将之前head节点指向当前节点的引用设置为null,之前的head对象就没有指向其他对象,也没有其他对象指向它
//那么根据GC Roots可达性分析,这样的对象会被gc
p.next = null; // help GC
return interrupted;
}
//AQS队列中已经有其他线程在排队了,那么当前线程节点park,而不用尝试tryAcquire(原因:前一个线程都还在排队,当前线程就没有必要去尝试获取锁),
//park的过程分为两步
// 1.将当前节点的前一个节点的waitStatus设置为-1
// 2.将当前线程park
if (shouldParkAfterFailedAcquire(p, node))//将当前线程的前一个节点的waitStatus设置为-1
//|= 相当于 !=
interrupted |= parkAndCheckInterrupt();//park当前线程
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
shouldParkAfterFailedAcquire(p, node):设置当前节点的前一个节点的waitStatus
p
rivate static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;//获取前一个节点的waitStatus
//ws = -1,表示当前线程是可以被唤醒的
if (ws == Node.SIGNAL)//在被其他线程设置为-1之后,该方法直接返回true
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {//线程节点第一次进入的时候,waitStatus=0 或者等于 -3
/*
* 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.
*/
//前一个线程的节点状态由当前线程设置
//原因:1.当前线程不能确定自己的线程状态,只要其他线程才能确定当前线程的状态
// 2.当前线程设置自己状态的过程可能会异常
pred.compareAndSetWaitStatus(ws, Node.SIGNAL);//将当前线程节点的前一个的waitStatus设置为-1
}
return false;
}
parkAndCheckInterrupt():将当前线程park,加锁过程完成
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);//park当前线程
//当前线程被unpark之后,继续从这里执行
//Thread.interrupted():
//如果当前线程被其他线程调用了interrupt方法,Thread.interrupted()返回true,同时将interrupt重置
return Thread.interrupted();
}
ReentrantLock的解锁过程
public void unlock() {
sync.release(1);
}
tryRelease(arg)://尝试释放锁
protected final boolean tryRelease(int releases) {//releases = 1
//如果state = 1,说明当前线程没有重入, 大于1说明线程重入
int c = getState() - releases;//释放lock之后,当前锁的状态 ,如果= 0 则当前线程不再持有锁,大于1说明当前线程依然持有锁
if (Thread.currentThread() != getExclusiveOwnerThread())//正常来说说不可能的
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//当前线程不再持有锁,将exclusiveOwnerThread设置为null,释放锁成功
free = true;
setExclusiveOwnerThread(null);
}
setState(c);//设置锁状态
return free;
}
release(int arg):释放锁
public final boolean release(int arg) {
//尝试释放锁,释放锁成功,tryRelease会返回true。这里有两种情况
//1.当前线程没有重入
//2.线程重入了,但是全部释放了。所以锁重入了多少次就要释放多少次。否则该线程就会一致持有锁
if (tryRelease(arg)) {
//唤醒下一个线程
Node h = head;
//对h != null && h.waitStatus != 0的情况分析
// 1. h == null,表明了AQS队列没有初始化过,对于AQS没有初始化的情况有两种
// a.只要一个线程获取锁
// b.多个线程在获取锁,但是它们直接是交替执行的,不需要进入AQS中排队等待。也就是说这些线程加锁时第一次tryAcquire时都会加锁成功
// 2. h != null && h.waitStatus != 0,表示了AQS已经被初始化过了,并且队列中还有等待唤醒的线程
//原因:h != null说明head是存在的
// h.waitStatus != 0 (也就是 = -1),因为节点的waitStatus是由下一个节点设置的,
//所以waitStatus不等于默认值0就说明了下一个节点是存在的
if (h != null && h.waitStatus != 0)
//唤醒下一个可被唤醒的节点
unparkSuccessor(h);
return true;
}
return false;
}
unparkSuccessor(h):唤醒头节点的下一个可被唤醒的节点(不一定是头节点的下一个)
private void unparkSuccessor(Node node) {//node = head
/*
* 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;
if (ws < 0)//重置head节点的waitStatus = 0
node.compareAndSetWaitStatus(ws, 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;//头节点的下一个节点
//这一块代码几乎可以不需要考虑,因为线程被cancel只会出现在我们自己实现AQS出异常的时候调用cancelAcquire,
//jdk实现的AQS不会出现
if (s == null || s.waitStatus > 0) {//下一个节点不存在或者是被canceled状态,就从队尾开始找最靠近头节点的可被唤醒的节点
s = null;
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null)
//唤醒线程
LockSupport.unpark(s.thread);
}
unparkSuccessor(h)执行完成之后,park的地方继续执行,继续执行tryAcquire(),尝试加锁过程。加锁成功后将当前唤醒的节点中thread设置为null。lock解锁完成
head中的thread=null有两层含义:1.空节点 2.当前持有锁的线程
总结:当第一个线程来请求加锁的时候,可以直接拿到锁,而没有去初始化AQS队列。当第二个线程请求加锁时候,判断当前锁有没有被某一个线程持有,如果没有其他线程持有,就去查看AQS队列中有没有线程在等待唤醒,如果AQS没有被初始化或者没有线程在等待唤醒,那么第二个线程就可以获取锁。如果其他线程已经持有了锁,再看第二个线程是不是当前持有锁的线程,如果是的话,当前线程也可以加锁成功,同时lock的state+1,表示锁的重入次数+1。如果当前持有锁的线程和第二个线程不是同一个线程,那么第二个线程要加入的AQS队列中去。在加入到队列的过程中,如果第二个线程排在第二位(head.next指向的节点)会去再次尝试获取锁,如果不是的话就直接加入到AQS队列中。加入AQS时会将当前节点的前一个节点的waitStatus设置为-1,同时将当前节点设置为tail,等待被唤醒。AQS队列中的节点在得到锁之后,会将原来的head节点引用断开,将当前节点设置为新的head,同时将节点的thread设置为null.
当持有锁的对象释放锁时,会讲head节点的waitStatus设置为0,同时查看AQS是否存在,或者AQS中是否有等待的线程,如果有等待的线程那么就唤醒第一个可被唤醒的线程。
非公平锁与公平锁加锁过程的区别
公平锁:公平锁加锁时会检查AQS中是否有排队等待唤醒的线程,如果有的话,当前线程会加入到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中是否有排队等待唤醒的队列,而是直接尝试获取锁,如果获取不到锁才会加入到AQS中排队
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(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;
}
公平锁与非公平锁的区别就是在没有线程持有锁的情况下,公平锁回去查看AQS中是否有线程在排队,如果有线程在排队,那么当前线程也加入排队。而非公平锁则是先进行一次尝试加锁,如果加锁失败了才会加入到AQS中排队