本文讲解了ReentrantLock是如何实现公平和非公平两种锁的原理。
点击上方“后端开发技术”,选择“设为星标” ,优质资源及时送达
上一篇文章,我详细讲解了ReentrantLock的加锁和解锁的原理,请自行阅读。
重点,一文掌握ReentrantLock加解锁原理!|原创
看完之后,大家可能有一个疑问。
为什么继承AQS实现的ReentrantLock,获取锁不成功都需要进入同步队列,那后续解锁并且争夺锁的过程就是有序的,为什么会有公平和不公平?
实际上进入队列中挂起的线程确实是公平的,差别在于在进入队列之前的抢占过程,如下图。
公平锁FairSync
和非公平锁NonfairSync
实现区别在于非公平锁会无视是否有已经排队的线程,直接与队头线程参与锁的竞争,具体细节如下:
1、 当有新的线程进入,非公平锁会直接加入争夺锁的过程,使用compareAndSetState()
方法直接CAS抢占。而公平锁不会抢占,直接调用acquire()
方法。
//非公平锁
final void lock() {// 直接参与争夺锁if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);
}
// 公平锁
final void lock() {acquire(1);
}
2、当初次尝试抢占失败的时候,非公平锁会在tryAcquire()
方法中再次尝试抢占。而公平锁会先使用hasQueuedPredecessors()
方法判断同步队列是否有前驱节点,如果没有的话才会加入抢占,否则进入队列排队。
//非公平锁
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) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
}
// 公平锁
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;
}
在进入队列参与锁竞争的逻辑之前讲过了,本文不再赘述。
最后,欢迎大家提问和交流。
如果对你有帮助,欢迎点赞、评论或分享,感谢阅读!
除了MVCC,Undo Log 还有哪些作用?|原创
2022-11-23
update在MySQL中是怎样执行的,一张图牢记|原创
2022-11-19
MySQL主从数据不一致,怎么办?
2022-11-15