Java并发编程--锁原理之独占锁ReentrantLock

朱雀 2023-07-01 15:53 88阅读 0赞

文章目录

  • 独占锁ReentrantLock的原理
      • (1). 结构
      • (2). 获取锁
        • 1). void lock()方法
        • 2). void lockInterruptibly()方法
        • 3). boolean tryLock()方法
        • 4). boolean tryLock(long timeout, TimeUnit unit)方法
      • (3). 释放锁
        • 1). void unlock()方法

独占锁ReentrantLock的原理

(1). 结构

 ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取这个锁,其他线程尝试获取就会被阻塞并放入AQS阻塞队列中,类图结构如下

在这里插入图片描述

 底层是使用AQS来实现的,根据参数来决定其内部是否公平内部类sync是NonfairSync类型的则非公平,如果是FairSync则公平

(2). 获取锁

1). void lock()方法

  1. public void lock() {
  2. sync.lock();
  3. }
  4. // 非公平锁
  5. final void lock() {
  6. if (compareAndSetState(0, 1))// 如果当前锁是自由的,就直接获取,这就是竞争锁
  7. setExclusiveOwnerThread(Thread.currentThread());
  8. else// 如果锁被占有,那么去排队
  9. acquire(1);
  10. }
  11. // 公平锁
  12. final void lock() {
  13. // 公平锁,就要排队
  14. acquire(1);
  15. }
  16. // 通用
  17. public final void acquire(int arg) {
  18. // 同之前AQS
  19. if (!tryAcquire(arg) &&
  20. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  21. selfInterrupt();
  22. }
  23. // 非公平锁
  24. // 非公平性体现在如果一个线程释放了锁,在进行下一步唤醒队列中的节点之前别的线程直接竞争到了锁,那么队列中的节点会唤醒失败
  25. protected final boolean tryAcquire(int acquires) {
  26. return nonfairTryAcquire(acquires);
  27. }
  28. final boolean nonfairTryAcquire(int acquires) {
  29. //先获取线程和锁的状态
  30. final Thread current = Thread.currentThread();
  31. int c = getState();
  32. // 这整个if-elseif都可以让没有进行排队直接获取锁的线程直接拿到锁
  33. if (c == 0) {
  34. // 如果锁空闲
  35. if (compareAndSetState(0, acquires)) {
  36. // 获取到锁
  37. setExclusiveOwnerThread(current);
  38. return true;
  39. }
  40. }else if (current == getExclusiveOwnerThread()) {
  41. //如果锁不空闲,但是是自己拿到的
  42. // 锁重入
  43. int nextc = c + acquires;
  44. if (nextc < 0) // overflow
  45. throw new Error("Maximum lock count exceeded");
  46. setState(nextc);
  47. return true;
  48. }
  49. // 锁不空闲而且不属于自己,就只能是别人的锁,尝试获取失败
  50. return false;
  51. }
  52. // 公平锁
  53. protected final boolean tryAcquire(int acquires) {
  54. final Thread current = Thread.currentThread();
  55. int c = getState();
  56. if (c == 0) {
  57. // 不同点就在这,hasQueuedPredecessors()方法中当前线程节点有前继节点返回true,AQS队列为空或当前线程是AQS的第一个节点返回false
  58. // 简单点说就是确保当前节点是头结点后的第一个节点,也就是排到队首的节点,保证只有队首节点才能被唤醒
  59. if (!hasQueuedPredecessors() &&
  60. compareAndSetState(0, acquires)) {
  61. setExclusiveOwnerThread(current);
  62. return true;
  63. }
  64. }
  65. // 如果当前节点已经获取了锁,直接重入,这就不牵扯公平性了
  66. else if (current == getExclusiveOwnerThread()) {
  67. int nextc = c + acquires;
  68. if (nextc < 0)
  69. throw new Error("Maximum lock count exceeded");
  70. setState(nextc);
  71. return true;
  72. }
  73. return false;
  74. }

 非公平锁当锁自由时,就可以直接获取锁,而公平锁需要去acquire()方法内部判断.

 两种锁调用的acquire()方法都一样.

 tryAcquire()方法有所区别,实现逻辑的区别在于公平锁在锁空闲的情况下,获取锁之前确保了当前节点是头结点后的第一个节点,从而让唤醒的节点一定是队列中排过队的.

2). void lockInterruptibly()方法

 对中断响应的获取锁.逻辑都类似,调用的是AQS中可被中断的获取锁方法

3). boolean tryLock()方法

 尝试获取锁,如果没获取到不会阻塞

  1. public boolean tryLock() {
  2. // 之前介绍过nonfairTryzaiAcquire(),就是单纯的获取锁,之前的阻塞的逻辑是在acquire中
  3. return sync.nonfairTryAcquire(1);
  4. }

4). boolean tryLock(long timeout, TimeUnit unit)方法

 设置了时间,如果在该时间内没有获取到锁,返回false.

 TimeUnit参数为时间粒度,直接new一个TimeUnit对象即可.

(3). 释放锁

1). void unlock()方法

  1. public void unlock() {
  2. sync.release(1);
  3. }
  4. public final boolean release(int arg) {
  5. // 逻辑同AQS
  6. if (tryRelease(arg)) {
  7. Node h = head;
  8. if (h != null && h.waitStatus != 0)
  9. unparkSuccessor(h);
  10. return true;
  11. }
  12. return false;
  13. }
  14. protected final boolean tryRelease(int releases) {
  15. // c为本次重入成功后,锁的重入次数
  16. int c = getState() - releases;
  17. // 如果不是当前线程拿到锁,抛出异常
  18. if (Thread.currentThread() != getExclusiveOwnerThread())
  19. throw new IllegalMonitorStateException();
  20. boolean free = false;
  21. // 如果这次重入之后,重入次数为零,证明锁没有被任何线程重入,清空锁持有线程
  22. if (c == 0) {
  23. free = true;
  24. setExclusiveOwnerThread(null);
  25. }
  26. // 设置重入次数
  27. setState(c);
  28. return free;
  29. }

最后用一幅图加深理解

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,88人围观)

还没有评论,来说两句吧...

相关阅读