说到ReentrantLock,就会说到AQS(AbstractQueuedSynchronizer),如果AQS不了解的朋友,最好去了解一下AQS,因为ReentrantLock内部只是去操作加锁,而真正加锁是控制AQS的state状态。
上图是ReentrantLock的部分实现。因为只讲到lock,我也就只会看lock方法相关的,里面有一个Sync的内部类,这个内部类继承自AQS,然后,外面有一个这个内部类的锁对象,在后面会用到,Sync类里面有一个lock的抽象方法是留给子类去实现的。子类就包括公平锁类和非公平锁类
这个是非公平锁的实现类NonfairSync,继承自Sync类。然后重写了它的lock加锁方法,在实现中因为是非公平的,所以一进来会先通过cas尝试将AQS类的state参数改为1,直接尝试加锁。如果尝试加锁失败会调用AQS的acquire方法继续尝试加锁
进入到acquire会先调用tryAcquire再次尝试加锁,而这个tryAcquire方法AQS其实是没有什么实现的,会调用到NonfairSync里面的tryAcquire,而tryAcquire实际会调用到Sync内部类里面的nonfairTryAcquire非公平尝试加锁方法。
其实这个nonfairTryAcquire方法在公平锁中也会调用到,这个等会说,在这个方法里面就是继续判断锁当前是否被占用,然后通过cas加锁。下面的逻辑是可重入逻辑,c就上当前锁的状态值,如果是有一个线程获得锁,就是1。如果同一线程多次重入,每重入一次+1,那也是就每次释放锁时都要调用unlock去-1。如果这里还是加锁失败,那acquire方法里面就会将当前线程放入同步队列。
首先调用addWaiter,根据指定模式创建指定的node节点,因为ReentrantLock是独占模式,所以传进去的EXCLUSIVE,这里通过当前线程和模式传入,初始化一个双向node节点,获取最后一个节点,根据最后一个节点是否存在来操作当前节点的父级。如果尾节点不存在会去调用enq去初始化
首先会去判断尾节点是否存在,不存在会去初始化一个新的节点给头节点,然后继续循环这个时候头节点已经存在,然后将头节点的next给当前节点。初始化完成以后就会调用acquireQueued方法将当前节点加入到等待队列。
这个方法一进来也会尝试将当前节点去加锁,然后如果加锁成功就将当前节点设置为头节点,最后将当前线程中断,等待唤醒。
最后还有一个很重要的操作,就是判断当前节点的父级节点的状态,如果父级状态是-1,则代表当前线程可以被唤醒了。如果父级的状态为取消状态就跳过父级,寻找下一个可以被唤醒的父级,然后绑定上节点关系,最后将父级的状态更改为-1