ReentrantLock是一个可重入的互斥锁,ReentrantLock将所有的锁操作都委派给了实现AQS的内部类Sync上,同时定义了Sync的两个子类分别提供对公平锁和非公平锁的支持,默认使用非公平锁。

点击查看【processon】

1、Sync,NofairSync和FairSync

ReentrantLock基于AQS独占锁的实现,Sync类继承了AQS,主要实现了其尝试加锁的tryAcquire方法,尝试释放锁的tryRelease 方法。Sync类实际是非公平实现,NofairSync类只是套了Sync类的壳子,公平锁实现FairSync主要是加锁时有所不同。


ReentrantLock默认为非公平锁实现,调用其有参构造指定fair为true则为公平锁实现。

  1. public ReentrantLock() {
  2. sync = new NonfairSync();
  3. }
  4. public ReentrantLock(boolean fair) {
  5. sync = fair ? new FairSync() : new NonfairSync();
  6. }

Sync

  1. abstract static class Sync extends AbstractQueuedSynchronizer {
  2. private static final long serialVersionUID = -5179523762034025860L;
  3. /**
  4. * 非公平锁加锁逻辑
  5. */
  6. final boolean nonfairTryAcquire(int acquires) {
  7. final Thread current = Thread.currentThread();
  8. int c = getState();
  9. if (c == 0) {
  10. //如果是无锁状态直接抢锁
  11. if (compareAndSetState(0, acquires)) {
  12. //抢锁成功则设置锁的占用线程
  13. setExclusiveOwnerThread(current);
  14. return true;
  15. }
  16. }
  17. //当前锁被占用但是占用线程和当前线程是同一线程则是锁重入
  18. else if (current == getExclusiveOwnerThread()) {
  19. int nextc = c + acquires;
  20. if (nextc < 0) // overflow
  21. throw new Error("Maximum lock count exceeded");
  22. setState(nextc);
  23. return true;
  24. }
  25. return false;
  26. }
  27. /**
  28. * 尝试释放锁
  29. * @param releases
  30. * @return
  31. * true 当前线程已经完全释放锁
  32. * false 当前线程未完全释放锁
  33. */
  34. @ReservedStackAccess
  35. protected final boolean tryRelease(int releases) {
  36. //当前值- 释放的值
  37. int c = getState() - releases;
  38. //当前线程不是持锁线程直接抛异常
  39. if (Thread.currentThread() != getExclusiveOwnerThread())
  40. throw new IllegalMonitorStateException();
  41. //是佛已经完全释放锁
  42. boolean free = false;
  43. if (c == 0) {
  44. //说明当前线程已经完全释放锁
  45. free = true;
  46. //将锁的独占线程设置为null
  47. setExclusiveOwnerThread(null);
  48. }
  49. //更新state的值
  50. setState(c);
  51. return free;
  52. }
  53. protected final boolean isHeldExclusively() {
  54. return getExclusiveOwnerThread() == Thread.currentThread();
  55. }
  56. final ConditionObject newCondition() {
  57. return new ConditionObject();
  58. }
  59. final Thread getOwner() {
  60. return getState() == 0 ? null : getExclusiveOwnerThread();
  61. }
  62. final int getHoldCount() {
  63. return isHeldExclusively() ? getState() : 0;
  64. }
  65. final boolean isLocked() {
  66. return getState() != 0;
  67. }
  68. private void readObject(java.io.ObjectInputStream s)
  69. throws java.io.IOException, ClassNotFoundException {
  70. s.defaultReadObject();
  71. setState(0); // reset to unlocked state
  72. }
  73. }

NonfairSync

  1. static final class NonfairSync extends Sync {
  2. private static final long serialVersionUID = 7316153563782823691L;
  3. protected final boolean tryAcquire(int acquires) {
  4. return nonfairTryAcquire(acquires);
  5. }
  6. }

FairSync

  1. static final class FairSync extends Sync {
  2. private static final long serialVersionUID = -3000897897090466540L;
  3. /**
  4. * Fair version of tryAcquire. Don't grant access unless
  5. * recursive call or no waiters or is first.
  6. */
  7. @ReservedStackAccess
  8. /**
  9. * 尝试抢锁 返回是否抢锁成功
  10. * 返回true
  11. * 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁成功
  12. * 2、AQS处于有锁状态,但当前线程和持有锁的线程为同一线程,为锁重入
  13. * 返回false
  14. * 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁失败
  15. * 2、AQS处于有锁状态,并且当前线程不是持有锁的线程
  16. */
  17. protected final boolean tryAcquire(int acquires) {
  18. final Thread current = Thread.currentThread();
  19. int c = getState();
  20. //c == 0 表明当前AQS处于无锁状态
  21. if (c == 0) {
  22. /**
  23. *
  24. * 条件1:!hasQueuedPredecessors() 因为当前为公平锁 任何时候都要检查一下 队列中是否有在当前线程前的等待者
  25. * true 说明当前线程前面没有等待者,直接尝试获取锁
  26. * false 说明当前线程前面有等待者,当前线程需要入队等待
  27. * 条件2:compareAndSetState(0, acquires) 尝试CAS获取锁
  28. * 前置条件(AQS处于无锁状态且当前线程为第一个请求锁的线程)
  29. * true 当前线程CAS抢锁成功
  30. * false 当前线程CAS抢锁失败
  31. *
  32. */
  33. if (!hasQueuedPredecessors() &&
  34. compareAndSetState(0, acquires)) {
  35. // 将持锁线程对象设置为当前线程
  36. setExclusiveOwnerThread(current);
  37. return true;
  38. }
  39. }
  40. //c != 0 表明当前AQS锁被占用
  41. else if (current == getExclusiveOwnerThread()) {
  42. //如果当前线程和持有锁的线程相同 则为锁重入
  43. int nextc = c + acquires;
  44. //越界判断:重入的深度很深 会导致 nextc < 0 ,int值达到最大值 再加一就为负数了
  45. if (nextc < 0)
  46. throw new Error("Maximum lock count exceeded");
  47. setState(nextc);
  48. return true;
  49. }
  50. /**
  51. * 执行到此处的情况:
  52. * 1、state == 0 ,CAS失败
  53. * 2、state >0 且当前线程不是持锁线程
  54. */
  55. return false;
  56. }
  57. }

2、加锁流程

只贴ReentrantLock相关代码,AQS相关代码参考AQS源码分析
AQS源码分析

ReentrantLock#lock

  1. public void lock() {
  2. sync.acquire(1);
  3. }

AQS#acquire

  1. /**
  2. * 阻塞等待获取锁
  3. * 1、AQS为无锁状态,第一次直接尝试抢锁然后成功
  4. * 2、AQS为无锁状态且AQS队列没有线程等待锁但是CAS抢锁失败 或者 AQS队列有线程等待锁,将当前线程封装成节点加入等待队列,
  5. * 然后在acquireQueued方法中发现前置节点为头节点,再次尝试抢锁,然后成功了
  6. * 3、AQS队列有线程等待锁,将当前线程封装成节点加入等待队列,然后线程被挂起了,唤醒(可能外部线程调用unpark或者中断)后继续抢锁,如果失败则继续挂起,直到抢锁成功
  7. * @param arg
  8. */
  9. public final void acquire(int arg) {
  10. /**
  11. *条件1: !tryAcquire(arg) 尝试获取锁 子类实现
  12. * 返回true 第一次尝试获取锁失败
  13. * 返回false 第一次尝试获取锁成功
  14. * 条件2: acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
  15. * 前置条件(第一次尝试获取锁失败)
  16. * addWaiter(Node.EXCLUSIVE) 将当前线程封装成node然后入队
  17. * acquireQueued 1、挂起当前线程 2、执行唤醒后相关逻辑
  18. * 返回true 挂起过程中线程被中断或者唤醒过
  19. * 返回false 未被中断过
  20. *
  21. */
  22. if (!tryAcquire(arg) &&
  23. acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){
  24. /**
  25. * 如果线程被挂起后被中断退出 再次设置中断标记
  26. */
  27. selfInterrupt();
  28. }
  29. }

2.1、公平锁加锁

ReentrantLock是一个可重入的互斥锁,所以有锁状态下,如果持锁线程和当前线程为同一线程,则为锁重入。公平锁抢锁时,需要满足两个条件,一是当前为无锁状态,二是队列中无等待线程,否则不能抢锁。

  1. /**
  2. * 尝试抢锁 返回是否抢锁成功
  3. * 返回true
  4. * 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁成功
  5. * 2、AQS处于有锁状态,但当前线程和持有锁的线程为同一线程,为锁重入
  6. * 返回false
  7. * 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁失败
  8. * 2、AQS处于有锁状态,并且当前线程不是持有锁的线程
  9. */
  10. protected final boolean tryAcquire(int acquires) {
  11. final Thread current = Thread.currentThread();
  12. int c = getState();
  13. //c == 0 表明当前AQS处于无锁状态
  14. if (c == 0) {
  15. /**
  16. *
  17. * 条件1:!hasQueuedPredecessors() 因为当前为公平锁 任何时候都要检查一下 队列中是否有在当前线程前的等待者
  18. * true 说明当前线程前面没有等待者,直接尝试获取锁
  19. * false 说明当前线程前面有等待者,当前线程需要入队等待
  20. * 条件2:compareAndSetState(0, acquires) 尝试CAS获取锁
  21. * 前置条件(AQS处于无锁状态且当前线程为第一个请求锁的线程)
  22. * true 当前线程CAS抢锁成功
  23. * false 当前线程CAS抢锁失败
  24. *
  25. */
  26. if (!hasQueuedPredecessors() &&
  27. compareAndSetState(0, acquires)) {
  28. // 将持锁线程对象设置为当前线程
  29. setExclusiveOwnerThread(current);
  30. return true;
  31. }
  32. }
  33. //c != 0 表明当前AQS锁被占用
  34. else if (current == getExclusiveOwnerThread()) {
  35. //如果当前线程和持有锁的线程相同 则为锁重入
  36. int nextc = c + acquires;
  37. //越界判断:重入的深度很深 会导致 nextc < 0 ,int值达到最大值 再加一就为负数了
  38. if (nextc < 0)
  39. throw new Error("Maximum lock count exceeded");
  40. setState(nextc);
  41. return true;
  42. }
  43. /**
  44. * 执行到此处的情况:
  45. * 1、state == 0 ,CAS失败
  46. * 2、state >0 且当前线程不是持锁线程
  47. */
  48. return false;
  49. }

2.2、非公平锁加锁

和公平锁的可重入逻辑相同,有锁状态下,如果持锁线程和当前线程为同一线程,则为锁重入。非公平锁抢锁时,只需要一个条件,当前为无锁状态,不用关系是队列中无等待线程。

NonfairSync#tryAcquire

  1. protected final boolean tryAcquire(int acquires) {
  2. return nonfairTryAcquire(acquires);
  3. }

Sync#nonfairTryAcquire

  1. /**
  2. * 非公平锁加锁逻辑
  3. */
  4. final boolean nonfairTryAcquire(int acquires) {
  5. final Thread current = Thread.currentThread();
  6. int c = getState();
  7. if (c == 0) {
  8. //如果是无锁状态直接抢锁
  9. if (compareAndSetState(0, acquires)) {
  10. //抢锁成功则设置锁的占用线程
  11. setExclusiveOwnerThread(current);
  12. return true;
  13. }
  14. }
  15. //当前锁被占用但是占用线程和当前线程是同一线程则是锁重入
  16. else if (current == getExclusiveOwnerThread()) {
  17. int nextc = c + acquires;
  18. if (nextc < 0) // overflow
  19. throw new Error("Maximum lock count exceeded");
  20. setState(nextc);
  21. return true;
  22. }
  23. return false;
  24. }

3、释放锁流程

ReentrantLock#unlock

  1. public void unlock() {
  2. sync.release(1);
  3. }

AQS#release

  1. /**
  2. * 释放锁
  3. * @param arg
  4. * @return
  5. */
  6. public final boolean release(int arg) {
  7. if (tryRelease(arg)) {
  8. Node h = head;
  9. /**
  10. * 条件1: h != null
  11. * head是懒加载的,head为null说明没有发生锁竞争,当前线程拿锁时就为空队列,一直没有创建head节点。只有线程尝试拿锁时,发现为无锁状态,并且队列为空队列,
  12. * 但是CAS抢锁时失败了,此时将当前线程封装为节点入队时首先就要创建一个head节点,然后将当前线程节点放在尾结点
  13. *
  14. * 条件2:h.waitStatus != 0
  15. * 返回true 当前节点后面一定插入过其他节点 当前线程需要唤醒后继节点
  16. */
  17. if (h != null && h.waitStatus != 0){
  18. //唤醒最近的后继节点
  19. unparkSuccessor(h);
  20. }
  21. return true;
  22. }
  23. return false;
  24. }

Sync#tryRelease

  1. /**
  2. * 尝试释放锁
  3. * @param releases
  4. * @return
  5. * true 当前线程已经完全释放锁
  6. * false 当前线程未完全释放锁
  7. */
  8. @ReservedStackAccess
  9. protected final boolean tryRelease(int releases) {
  10. //当前值- 释放的值
  11. int c = getState() - releases;
  12. //当前线程不是持锁线程直接抛异常
  13. if (Thread.currentThread() != getExclusiveOwnerThread())
  14. throw new IllegalMonitorStateException();
  15. //是否已经完全释放锁
  16. boolean free = false;
  17. if (c == 0) {
  18. //说明当前线程已经完全释放锁
  19. free = true;
  20. //将锁的独占线程设置为null
  21. setExclusiveOwnerThread(null);
  22. }
  23. //更新state的值
  24. setState(c);
  25. return free;
  26. }