package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;
/*
* 阅读本文前,最好先了解一下CAS和AQS,然后有疑问下方给我留言,大家一起学习
* 不要随便Copy别人的博客,这个习惯不好
* ReentrantLock中实现了两套锁机制
* 一个是NonfairSync(非公平锁),另一个是FairSync(公平锁)
* 公平与非公平不在于如何改变队列中排队线程的优先级,
* 而在于新的线程来了,是否可以发生抢占
* 非公平锁优先让加入线程抢占锁,抢占失败后交给AQS入队处理
* 而公平锁直接交给AQS处理
*/
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
// 锁的类型,公平锁和非公平锁都继承了这个抽象内部类
private final Sync sync;
// 抽象内部类继承了AQS,强烈建议子路老师的AQS视频(自己看很难学的明白)
// 你会发现这个类中的nonfairTryAcquire是专为非公平锁设计的
// 前面说过了,非公平多了一段抢占的逻辑代码,而不是直接交给AQS处理
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
// 加锁方法,子类需要实现这个方法
abstract void lock();
// 非公平的尝试获取锁,注意,进到这个代码块里的可不止一个线程
final boolean nonfairTryAcquire(int acquires) {
// 获取当前工作的线程
final Thread current = Thread.currentThread();
// 获取锁的状态 0表示没有线程获取锁,大于0表示锁已被占用
// 为什么不直接说1而是说大于0呢,因为可重入锁允许持有锁的线程
// 再次获取锁,所以可能 0 + 1 + 1 ..... 就是一个大于0的数了
int c = getState();
// 如果锁没有其它线程占用
// 注意,由于是多线程,进入该代码块的线程不止一个
// 由于指令重排,c的值还没有来得及被修改,
// 就被好多线程进行比较,全部返回true
if (c == 0) {
// CAS的算法修改c的值(原子修改c的值)
// 注意,进入该代码块的线程只有一个,就是修改c值成功的那个
if (compareAndSetState(0, acquires)) {
// 把当前的线程持有者设置为current,也就是进入了该代码块的线程
setExclusiveOwnerThread(current);
// 成功返回
return true;
}
}
// 跳到这里,说明上面的if不满足,也就是锁被占用了
// 锁被占用了,要看看是哪个线程占用的锁,如果是正在工作的线程再一次
// 获取锁,是允许它再一次获取锁的,这个就叫可重入,当然
// 上了几次锁,解锁的时候就要解几次锁