- ReentrantLock是基于AQS实现的可重入锁,比synchronized更灵活,可以设置超时时间,可以中断,支持公平和非公平锁两种方式。
- 公平锁获取锁的方式:
- 主要就是这三步
- 第一步:tryAcquire 先尝试获得锁
- 先获取state,情况一:如果state=0,那么说明当前没有线程占着锁,如果当前线程不需要排队,那么就可以尝试CAS修改state的状态,如果CAS修改成功,就说明抢锁成功,就把setExclusiveOwnerThread设置成当前线程。程序到这里就结束啦~
- hasQueuedPredecessors()判定是否需要排队。官方解释,如果队列中有线程排在你前面,那你就需要排队;否则当前线程就在队列的头的位置,或者队列是空的,你就可以起CAS抢锁。保证了公平性!!!
- 先获取state,情况一:如果state=0,那么说明当前没有线程占着锁,如果当前线程不需要排队,那么就可以尝试CAS修改state的状态,如果CAS修改成功,就说明抢锁成功,就把setExclusiveOwnerThread设置成当前线程。程序到这里就结束啦~
- 情况二:如果获得state!=0,但是被自己(当前线程占用),可以直接把state加1。程序到这里就结束啦~
- 其他情况:都认为尝试获取锁失败。那就要走第二步。
- 第二步:addWaiter 把当前线程当作节点,放到等待队列的尾部
- 第三步:acquireQueued 无限循环,执行排队操作,也就是阻塞线程,直到获得锁
- 如果当前线程已经排在队列头部,就尝试获得锁。获得成功程序到这就结束啦~
- 如果没有获得锁,就执行shouldParkAfterFailedAcquire ,如果排在当前线程前面的节点状态是signal,就返回true;如果排在前面的节点是CANCELLED,把前面等待状态是CANCELLED的全部清理掉,并把当前线程的前一节点设置成signal状态,就返回false(注意这里是无限循环,下次循环就会返回true);
- 这里返回false的好处是当前线程不需要立刻进入等待状态,可以等其他条件变化后,再判断是否进行等待状态。其次返回false,也不需要走后面的park方法啦
- 总之,这一步就是阻塞当前线程,排队等待,直到自己排到队首。
- 那么问题来了,我怎么能排到队首?当一个线程释放锁的时候,它会将当前头节点的下一个等待节点设置为新的头节点,并且唤醒这个新的头节点。
- 总结:公平锁lock的步骤简单讲就是,如果你不需要排队尝试获得锁,否则就排队阻塞直到获得锁。
- 非公平锁
- 非公平锁在一开始比公平锁多了一步CAS抢锁的步骤。通俗讲可以插队。