AQS、ReentrantLock、ReentrantReadWriteLock 结构与源码分析

不念不忘少年蓝@ 2021-11-16 10:54 479阅读 0赞

文章目录

      • 1 Lock 接口分析
      • 2 AQS 抽象类结构分析
      • 3 AQS 子类 —— ReentrantLock 结构分析
      • 4 AQS 子类 —— ReentrantReadWriteLock 结构分析
      • 5 ReentrantLock 源码分析
        • 5.1 非公平锁的获取与释放
        • 5.2 公平锁的获取与释放
      • 6 ReentrantReadWriteLock 源码分析
        • 6.1 非公平共享锁的获取与释放
        • 6.1 非公平独占锁的获取与释放
      • 7 lock、tryLock()、lockInterruptibly() 的区别
      • 8 Lock 与 Synchronized 的区别
        • 8.1 Synchronized 的优缺点
        • 8.2 Lock 的优缺点

1 Lock 接口分析


































Method Descript
void lock() 获取锁(获取不到一直等待)
boolean tryLock() 获取锁(获取不到返回 false)
boolean tryLock(long,unit) 获取锁(指定时间内获取不到返回 false)
void lockInterruptibly() 获取锁(当线程被终止时,退出等待)
void unlock() 释放锁
Condition new Condition() 与 Lock 配合使用,提供多个等待集合,更精确的控制,底层是 park/unpark 机制。同一个 lock 可以有多个 condition
  • Condition 实现类由 AQS 提供了,具体源码逻辑留待以后分析。。

2 AQS 抽象类结构分析

  • AQS 实现了一个等待队列,用来装没有获取到锁的线程
  • AQS 只实现了 没有获取锁、成功获取锁、没有释放锁、成功释放锁 后的逻辑(如何加入等待队列,如何从等待队列唤醒节点),具体的怎样获取,怎样释放,由子类实现
  • 因为 获取共享锁和获取独占锁 后的逻辑不太一样,AQS 需要分别为它们实现了相应的逻辑
  • 因为公平锁、非公平锁只和 如何获取锁的逻辑 有关,对 AQS 来说是透明的,所以 AQS 不需要额外区分
  • 子类 ReentrantLock:实现了公平独占锁、非公平独占锁
  • 子类 ReentrantReadWriteLock:实现了公平(独占锁/共享锁)、非公平(独占锁/共享锁)
  • 这里只简单的介绍 AQS 中的方法,具体的源码逻辑留待以后分析

    public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {

    1. //等待队列节点类,通过这个类组成了一个等待队列
    2. //等待队列是 CLH 锁队列的一种变体,CLH 锁常用于自旋锁。而我们却用来实现阻塞锁,通过使用相同的策略:在节点的前节点的线程中保存一些控制信息
    3. //每个节点的 status 字段用来跟踪一个线程是否应该被阻塞。但一个节点的前一个节点被释放时,该节点将被通知。队列的每个节点都充当一个特定通知样式的监视器,其中包含一个等待的线程。status 字段并不控制线程是否被授予锁。
    4. //线程可能会尝试获取它是否在队列中的第一个。但是队列头部的线程不一定能获取锁成功,也可能重新等待
    5. static final class Node{ ...}
    6. //队列的头部
    7. private transient volatile Node head;
    8. //队列的尾部
    9. private transient volatile Node tail;
    10. //锁的状态,分为高16和低16两把锁
    11. private volatile int state;
    12. //自旋 1000 纳秒
    13. static final long spinForTimeoutThreshold = 1000L;
    14. //--------------------- 独占模式下,获取锁失败后,如何进入队列(acquireAueued())中的逻辑
    15. //独占不响应中断模式下,获取锁失败后,如何加入队列
    16. public final void acquire(int arg){ ...}
    17. //独占响应中断模式下,获取锁失败后,如何加入队列
    18. public final void acquireInterruptibly(int arg){ ...}
    19. //独占有时间限制模式,获取锁失败后,如何加入队列
    20. public final boolean tryAcquireNanos(int arg, long nanosTimeout){ ...}
    21. //独占模式释放锁,释放成功后,如何通知等待队列
    22. public final boolean release(int arg){ ...}
    23. //被 acquire 调用,如果被中断返回 true
    24. final boolean acquireQueued(final Node node, int arg){ ...}
    25. //被 acquireInterruptibly 调用
    26. private void doAcquireInterruptibly(int arg){ ...}
    27. //被 tryAcquireNanos 调用,规定时间内获取失败返回 false,成功返回 true
    28. private boolean doAcquireNanos(int arg, long nanosTimeout){ ...}
  1. //--------------------- 共享模式下,获取锁失败后,如何进入队列中的逻辑
  2. //共享不响应中断模式下,获取锁失败后,如何加入队列
  3. public final void acquireShared(int arg){ ...}
  4. //共享响应中断模式下,获取锁失败后,如何加入队列
  5. public final void acquireSharedInterruptibly(int arg){ ...}
  6. //共享有时间限制模式下,获取锁失败后,如何加入队列
  7. public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout){ }
  8. //共享模式释下,释放锁成功后,如何通知等待队列
  9. public final boolean releaseShared(int arg)
  10. //--------------------- 被上面的代码调用 ---------------
  11. private void doAcquireShared(int arg){ ...}
  12. private void doAcquireSharedInterruptibly(int arg){ ...}
  13. private boolean doAcquireSharedNanos(int arg, long nanosTimeout){ ...}
  14. private void doReleaseShared(){ ...}
  15. //--------------------- 真正获取锁的逻辑---------由子类自定义
  16. //尝试获取独占锁,需要具体子类实现
  17. protected boolean tryAcquire(int arg);
  18. //尝试释放独占锁,需要具体子类实现
  19. protected boolean tryRelease(int arg);
  20. //尝试获取共享锁,需要具体子类实现
  21. protected int tryAcquireShared(int arg);
  22. //尝试释放共享锁,需要具体子类实现
  23. protected boolean tryReleaseShared(int arg);
  24. //---------------------------等待队列辅助方法
  25. //插入节点到队列中
  26. private Node enq(final Node node){ ...}
  27. //通过指定的 node 状态(SHARED/EXCLUSIVE)和当前线程,创建一个 node,并加入队列(里面调用了 enq 方法)
  28. private Node addWaiter(Node mode){ ...}
  29. //取消该节点获取锁
  30. private void cancelAcquire(Node node){ ...}
  31. //唤醒后继节点
  32. private void unparkSuccessor(Node node){ ...}
  33. //检查并且更新没有获取锁成功的节点的 status,返回 true 则说明线程需要 阻塞
  34. private static boolean shouldParkAfterFailedAcquire(Node pred, Node node){ ...}
  35. //挂起当前线程,返回线程中断状态,并清除中断状态
  36. private final boolean parkAndCheckInterrupt(){ ...}
  37. //Condition 实现类,基本所有的 condition 都使用的是它!
  38. public class ConditionObject implements Condition, java.io.Serializable{ ...}
  39. }

3 AQS 子类 —— ReentrantLock 结构分析

  • ReentrantLock 实现了获取/释放独占锁的逻辑,和 AQS 结合就形成了完整的锁

    //实现了 Lock 接口,客户只能调用 Lock 接口中的方法
    public class ReentrantLock implements Lock, java.io.Serializable {

    1. //内部实现了公平锁和非公平锁
    2. private final Sync sync;
    3. abstract static class Sync extends AbstractQueuedSynchronizer{ ...}
    4. static final class NonfairSync extends Sync{ ...}
    5. static final class FairSync extends Sync{ ...}
    6. //-------------------- Lock 接口中的方法,供客户调用-----------
    7. //-------------------- 内部统一调用 sync 类中的方法,内有公平锁、非公平锁供选择-----------
    8. //获取锁,不响应中断
    9. public void lock(){ ...}
    10. //获取锁,线程被中断,就会释放锁(如果持有锁),并抛异常
    11. public void lockInterruptibly(){ ...}
    12. //尝试获取锁,失败返回 false,不响应中断
    13. public boolean tryLock(){ ...}
    14. //规定时间内尝试获取锁,失败返回 false,响应中断
    15. public boolean tryLock(long timeout, TimeUnit unit){ ...}
    16. //释放锁
    17. public void unlock(){ ...}

    }

4 AQS 子类 —— ReentrantReadWriteLock 结构分析

  1. public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
  2. //----------------- 实现了共享锁、独占锁,这两把锁共存
  3. private final ReentrantReadWriteLock.ReadLock readerLock;
  4. private final ReentrantReadWriteLock.WriteLock writerLock;
  5. public static class ReadLock implements Lock, java.io.Serializable{ ...}
  6. public static class WriteLock implements Lock, java.io.Serializable{ ...}
  7. //----------------- 实现了公平锁、非公平锁,只能指定一个
  8. //-----------------
  9. final Sync sync;
  10. abstract static class Sync extends AbstractQueuedSynchronizer { }
  11. //很有意思的是,ReentrantReadWriteLock 将公平锁、非公平锁的区别仅仅归纳到了两个方法!
  12. //即,公平独占锁和非公平独占锁、公平共享锁和非公平共享锁,之间的区别,都仅仅是两个方法的区别!
  13. static final class NonfairSync extends Sync{ ...}
  14. static final class FairSync extends Sync{ ...}
  15. }

5 ReentrantLock 源码分析

  • Lock lock0 = new ReentrantLock() – 非公平锁
  • Lock lock1 = new ReentrantLock(true) – 公平锁

5.1 非公平锁的获取与释放

  • 调用 lock0.lock() 获取非公平锁
  • 首先 CAS 将独占锁设置为当前线程,不成功再走 AQS 进入阻塞队列流程
  • AQS 流程:首先调用 子类的 tryAcquire() 操作,尝试获取锁,不成功则进入等待队列
  • 在释放锁之前可以先判断下获取锁的线程是否是当前线程,避免走异常流程

    public void lock() { sync.lock();}
    final void lock() {

    1. //直接 CAS,成功则将独占锁标志设为当前线程
    2. if (compareAndSetState(0, 1))
    3. setExclusiveOwnerThread(Thread.currentThread());
    4. else
    5. //失败,走 AQS 加入等待队列逻辑,AQS 中也进行了几次 tryAcquire() 尝试获取锁,仍不成功,才进入等待队列的
    6. acquire(1);

    }
    // AQS 中又先尝试了 tryAcquire() 操作获取锁,不成功则加入等待队列
    public final void acquire(int arg) {

    1. if (!tryAcquire(arg) &&
    2. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    3. selfInterrupt();

    }
    protected final boolean tryAcquire(int acquires) {

    1. return nonfairTryAcquire(acquires);

    }
    final boolean nonfairTryAcquire(int acquires) {

    1. final Thread current = Thread.currentThread();
    2. int c = getState();
    3. //如果 state 为 0,说明没有线程获取锁,再一次 CAS 设值
    4. if (c == 0) {
    5. if (compareAndSetState(0, acquires)) {
    6. setExclusiveOwnerThread(current);
    7. return true;
    8. }
    9. }
    10. //如果已有线程持有锁,判断是否是当前线程,进行重入锁操作
    11. else if (current == getExclusiveOwnerThread()) {
    12. int nextc = c + acquires;
    13. if (nextc < 0) // overflow
    14. throw new Error("Maximum lock count exceeded");
    15. setState(nextc);
    16. return true;
    17. }
    18. //没有获取锁,则返回 false 尝试加入等待队列
    19. return false;

    }

  • 调用 lock0.unlock() 释放锁

    //直接走 AQS 中的 release() 流程
    public void unlock() { sync.release(1);}
    // AQS
    public final boolean release(int arg) {

    1. if (tryRelease(arg)) {
    2. Node h = head;
    3. if (h != null && h.waitStatus != 0)
    4. //释放锁成功,且头结点满足一点条件,则唤醒头结点的下一个节点
    5. unparkSuccessor(h);
    6. return true;
    7. }
    8. //释放不成功返回 false
    9. return false;

    }

    protected final boolean tryRelease(int releases) {

    1. int c = getState() - releases;
    2. //如果当前线程不是独占锁线程,抛锁状态异常,所以在释放锁之前可以先判断下,避免走异常流程
    3. //什么情况下会出现这种状况呢?响应中断的锁被中断了,如果在 finally 块中执行了 release 方法,就会触发这个异常!!可以对中断锁单独的 try/catch/finally 处理,这样就不用在 unlock() 方法中,每次都判断是否是当前线程了
    4. //不响应中断的锁,走到这步不可能触发这个异常,除非乱写,走来就 unlock()。。
    5. //所以,所有的 unlock() 方法都先判断是否是获取锁的线程,是最稳妥的!!等需要优化的时候在说嘛,不要过分设计
    6. if (Thread.currentThread() != getExclusiveOwnerThread())
    7. throw new IllegalMonitorStateException();
    8. boolean free = false;
    9. if (c == 0) {
    10. free = true;
    11. setExclusiveOwnerThread(null);
    12. }
    13. setState(c);
    14. return free;

    }

5.2 公平锁的获取与释放

  • 调用 lock1.lock() 获取公平锁
  • 和公平锁的区别在于

    • 非公平锁当前线程先直接尝试 CAS 获取独占锁,对于早已进入等待队列的线程来说,就不公平了
    • 并且非公平锁在 tryAquire() 时,也是先直接 CAS 的
    • 公平锁则直接走 AQS 流程,而且 tryAquire() 的时候还要判断等待队列中有没有线程在它前面,所以所有的线程是按顺序进,按顺序出的,很公平
    • 综上:区别在于 lock() 和 tryAcquire() 内部代码的不同
  • 公平锁和非公平锁的释放都是一样的流程,并没有区别

    public void lock() { sync.lock();}
    //公平锁的 lock() 操作,直接走 AQS 流程!
    final void lock() {

    1. acquire(1);

    }
    protected final boolean tryAcquire(int acquires) {

    1. final Thread current = Thread.currentThread();
    2. int c = getState();
    3. if (c == 0) {
    4. //先判断等待队列中有没有线程在它前面!
    5. if (!hasQueuedPredecessors() &&
    6. compareAndSetState(0, acquires)) {
    7. setExclusiveOwnerThread(current);
    8. return true;
    9. }
    10. }
    11. else if (current == getExclusiveOwnerThread()) {
    12. int nextc = c + acquires;
    13. if (nextc < 0)
    14. throw new Error("Maximum lock count exceeded");
    15. setState(nextc);
    16. return true;
    17. }
    18. return false;

    }

6 ReentrantReadWriteLock 源码分析

  • ReadWriteLock lock = new ReadWriteLock() – 获取非公平读写锁

6.1 非公平共享锁的获取与释放

  • 调用 lock.readLock().lock(),获取读锁

    • 如果独占锁被别的线程持有,则获取失败
    • 如果等待队列第一个不是写线程,则尝试 CAS,成功就获取成功
    • 否则走 fullTryAcquireShared() 流程,直到成功或失败为止
  • 这里有两个关键点:

    • 独占锁被别的线程获取,则走 AQS 流程
    • 等待队列第一个是写线程的话,如果当前线程不是重入锁,就乖乖走 AQS 流程
    • 关键:这样才能保证写线程不会一直被读线程阻塞,可以想象写线程很容易就成为第一个等待队列节点的
  • 如果读线程没有获取到锁,且等待队列第一个不是写线程,则读线程是不会加入等待队列的哦

    public void lock() {

    1. sync.acquireShared(1);

    }
    //走 AQS 流程
    public final void acquireShared(int arg) {

    1. //走子类 tryAcquireShared() 操作尝试获取锁,获取失败,加入等待队列
    2. if (tryAcquireShared(arg) < 0)
    3. doAcquireShared(arg);

    }
    protected final int tryAcquireShared(int unused) {

    1. Thread current = Thread.currentThread();
    2. int c = getState();
    3. //如果独占锁已被别的线程持有,则获取锁失败
    4. if (exclusiveCount(c) != 0 &&
    5. getExclusiveOwnerThread() != current)
    6. return -1;
    7. int r = sharedCount(c);// 状态变量 state 的高16位表示读线程的数量!
    8. //判断等待队列的头一个节点是不是写线程,如果不是且读线程数小于最大(因为16位的限制)的,并且 CAS 成功,获取锁成功
    9. if (!readerShouldBlock() &&
    10. r < MAX_COUNT &&
    11. compareAndSetState(c, c + SHARED_UNIT)) { //cas 将读锁的数量加1
    12. //关键点1:因为 上面的 cas 保证了只有一个线程走 r==0 的流程
    13. //关键点2:仔细观察,会发现 firstReader/firstReaderHoldCount 这两个变量只可能被第一个获取读锁的人访问到!!所以根本不需要同步的!!
    14. if (r == 0) {
    15. firstReader = current;
    16. firstReaderHoldCount = 1;
    17. } else if (firstReader == current) {
    18. firstReaderHoldCount++;
    19. } else {
    20. //这里使用了 ThreadLocal 思想,避免了同步,,
    21. HoldCounter rh = cachedHoldCounter;
    22. //rh.tid != getThreadId(current):这里保证了每个线程使用的 cacheHoldCounter 都是自己独有的,有意思,用这个方法 + threadLocal 保证了 全局变量的线程独有特性!!!
    23. if (rh == null || rh.tid != getThreadId(current))
    24. //走到这里说明读取到了别的线程的值,所以要获取当前线程的值
    25. cachedHoldCounter = rh = readHolds.get();
    26. else if (rh.count == 0)
    27. readHolds.set(rh);//确保设置了 threadId
    28. rh.count++;//每个线程的重入次数!这里没有在 else 语句中哦
    29. //这里我有个疑问:如果当前线程的 rh 值在工作内存中丢失了,如果这时去主内存取的话,那不就要重置次数了吗?
    30. }
    31. return 1;
    32. }
    33. //如果以上捷径没成功的话,走这里
    34. return fullTryAcquireShared(current);

    }
    //一直循环到,写锁被别的线程持有或等待队列第一个是写锁,first reader 不是自己且不是重入。返回 -1
    //CAS 成功返回 1
    //即如果是重入获取读锁,则要一直循环到写锁被别的线程持有或自己 CAS 成功才会返回
    //不是重入锁,直接返回
    final int fullTryAcquireShared(Thread current) {

    1. HoldCounter rh = null;
    2. for (;;) { //这个 for 循环一直到底
    3. int c = getState();
    4. if (exclusiveCount(c) != 0) {
    5. //如果写锁已被别的线程持有,还是直接失败
    6. if (getExclusiveOwnerThread() != current)
    7. return -1;
    8. // else we hold the exclusive lock; blocking here
    9. // would cause deadlock.
    10. } else if (readerShouldBlock()) {
    11. //等待队列的第一个是写线程
    12. // Make sure we're not acquiring read lock reentrantly
    13. if (firstReader == current) {
    14. // assert firstReaderHoldCount > 0;
    15. } else {
    16. //且写线程不是自己
    17. if (rh == null) {
    18. rh = cachedHoldCounter;
    19. if (rh == null || rh.tid != getThreadId(current)) {
    20. //获取自己的 cacheHoldCounter
    21. rh = readHolds.get();
    22. //这是干啥
    23. if (rh.count == 0)
    24. readHolds.remove();
    25. }
    26. }
    27. //不是重入锁直接返回
    28. if (rh.count == 0)
    29. return -1;
    30. }
    31. }
    32. if (sharedCount(c) == MAX_COUNT)
    33. throw new Error("Maximum lock count exceeded");
    34. //再一次尝试 CAS,这个 if 到底
    35. if (compareAndSetState(c, c + SHARED_UNIT)) {
    36. if (sharedCount(c) == 0) {
    37. firstReader = current;
    38. firstReaderHoldCount = 1;
    39. } else if (firstReader == current) {
    40. firstReaderHoldCount++;
    41. } else {
    42. if (rh == null)
    43. rh = cachedHoldCounter;
    44. if (rh == null || rh.tid != getThreadId(current))
    45. rh = readHolds.get();
    46. else if (rh.count == 0)
    47. readHolds.set(rh);
    48. rh.count++;
    49. cachedHoldCounter = rh; // cache for release
    50. }
    51. return 1;
    52. }
    53. }

    }

  • 调用 lock.readLock().unlock(),释放读锁

  • 走重入流程,且只有所有的读线程都释放了,才算读锁释放了!

    public void unlock() { sync.releaseShared(1);}
    //走 AQS 流程
    public final boolean releaseShared(int arg) {

    1. //走子类 tryReleaseShared() 操作
    2. if (tryReleaseShared(arg)) {
    3. //成功后再走 AQS 流程
    4. doReleaseShared();
    5. return true;
    6. }
    7. return false;

    }
    protected final boolean tryReleaseShared(int unused) {

    1. Thread current = Thread.currentThread();
    2. //重入锁逻辑
    3. if (firstReader == current) {
    4. if (firstReaderHoldCount == 1)
    5. firstReader = null;
    6. else
    7. firstReaderHoldCount--;
    8. } else {
    9. HoldCounter rh = cachedHoldCounter;
    10. if (rh == null || rh.tid != getThreadId(current))
    11. rh = readHolds.get();
    12. int count = rh.count;
    13. if (count <= 1) {
    14. readHolds.remove();
    15. if (count <= 0)
    16. throw unmatchedUnlockException();
    17. }
    18. --rh.count;
    19. }
    20. //只有所有的读线程都释放了,才算读锁释放了!
    21. for (;;) {
    22. int c = getState();
    23. int nextc = c - SHARED_UNIT;
    24. if (compareAndSetState(c, nextc))
    25. // Releasing the read lock has no effect on readers,
    26. // but it may allow waiting writers to proceed if
    27. // both read and write locks are now free.
    28. return nextc == 0;
    29. }

    }

6.1 非公平独占锁的获取与释放

  • 调用 lock.writeLock().lock(),获取写锁

    • 如果既没有读线程也没有写线程获取到锁,则直接 CAS,成功则获取,不成功则走 AQS 流程
    • 如果读锁不为0,直接失败,走 AQS 流程
    • 如果读锁为0,写线程不是当前线程,则失败。如果是,则重入

    public void lock() { sync.acquire(1);}
    //走 AQS 流程
    public final void acquire(int arg) {

    1. //走子类 tryAcquire() 流程
    2. if (!tryAcquire(arg) &&
    3. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    4. selfInterrupt();

    }
    protected final boolean tryAcquire(int acquires) {

    1. Thread current = Thread.currentThread();
    2. int c = getState();
    3. int w = exclusiveCount(c);
    4. //c 不为0,说明有读线程或写线程成功获取锁了
    5. if (c != 0) {
    6. // (Note: if c != 0 and w == 0 then shared count != 0)
    7. //写锁等于0,说明读锁不为0,直接失败
    8. //如果写锁不等于0,说明读锁为0。如果其它线程获取到了写锁,则直接失败
    9. if (w == 0 || current != getExclusiveOwnerThread())
    10. return false;
    11. //当前线程获取了读锁,则重入,返回成功
    12. if (w + exclusiveCount(acquires) > MAX_COUNT)
    13. throw new Error("Maximum lock count exceeded");
    14. // Reentrant acquire
    15. setState(c + acquires);
    16. return true;
    17. }
    18. //非公平锁,第一个方法始终返回false
    19. //如果 c==0,则直接 CAS,成功则获取成功,失败走 AQS 流程
    20. if (writerShouldBlock() ||
    21. !compareAndSetState(c, c + acquires))
    22. return false;
    23. setExclusiveOwnerThread(current);
    24. return true;

    }

  • 调用 lock.writeLock().unlock(),获取写锁

    public void unlock() { sync.release(1);}
    //走 AQS 流程,和重入锁一样
    public final boolean release(int arg) {

    1. if (tryRelease(arg)) {
    2. Node h = head;
    3. if (h != null && h.waitStatus != 0)
    4. unparkSuccessor(h);
    5. return true;
    6. }
    7. return false;

    }
    protected final boolean tryRelease(int releases) {

    1. //不是当前线程持有写锁,抛异常
    2. if (!isHeldExclusively())
    3. throw new IllegalMonitorStateException();
    4. //重入为 0,才释放写锁
    5. int nextc = getState() - releases;
    6. boolean free = exclusiveCount(nextc) == 0;
    7. if (free)
    8. setExclusiveOwnerThread(null);
    9. setState(nextc);
    10. return free;

    }

7 lock、tryLock()、lockInterruptibly() 的区别

  • lock():线程不获取到锁会一直阻塞,并且线程不响应中断
  • tryLock():线程尝试获取锁,获取不成功返回false,线程不会阻塞,并且不会响应中断
  • lockInterruptibly():线程不获取到锁会被阻塞,但是会响应中断,以下源码分析如何响应中断的
  • 调用lock.lockInterruptibly() 获取可响应中断的锁
  • 可以看出,响应中断与否,是在 AQS 中实现的,子类并不需要实现

    Lock lock = new ReentrantLock();
    lock.lockInterruptibly();
    public void lockInterruptibly() throws InterruptedException {

    1. sync.acquireInterruptibly(1);

    }
    public final void acquireInterruptibly(int arg)

    1. throws InterruptedException {
    2. //如果线程处于中断状态,清除中断状态并抛异常
    3. if (Thread.interrupted())
    4. throw new InterruptedException();
    5. if (!tryAcquire(arg))
    6. //这里是关键的代码
    7. doAcquireInterruptibly(arg);

    }
    private void doAcquireInterruptibly(int arg)

    1. throws InterruptedException {
    2. final Node node = addWaiter(Node.EXCLUSIVE);
    3. boolean failed = true;
    4. try {
    5. for (;;) {
    6. final Node p = node.predecessor();
    7. if (p == head && tryAcquire(arg)) {
    8. setHead(node);
    9. p.next = null; // help GC
    10. failed = false;
    11. return;
    12. }
    13. if (shouldParkAfterFailedAcquire(p, node) &&
    14. parkAndCheckInterrupt())
    15. //线程走到这里,说明被中断了(parkAndCheckInterrupt 返回 true 了)
    16. throw new InterruptedException();
    17. }
    18. } finally {
    19. if (failed)
    20. cancelAcquire(node);
    21. }

    }
    //关键:当线程被中断时,线程会从 park() 中醒来!!
    //这时因为线程处于中断状态, Thread.interrupted() == true !!
    private final boolean parkAndCheckInterrupt() {

    1. LockSupport.park(this);
    2. return Thread.interrupted();

    }

8 Lock 与 Synchronized 的区别

8.1 Synchronized 的优缺点

优点:

  • 使用简单:语义清晰,且由虚拟机来释放锁,不需要人为操作
  • 由 JVM 提供,会持续不断地进行优化,目前已经提供了多种优化方案(锁粗化、锁消除、轻量级锁、偏向锁)

缺点:

  • 功能单一(使用简单带来的副作用),无法实现一些锁的高级特性:公平锁、中断锁、超时锁、读写锁

8.2 Lock 的优缺点

优点:

  • 由 JDK 提供,可以实现很多高级特性(见 Synchronized 的缺点)
  • 可以实现自定义锁(通过继承 AQS)

缺点:

  • 使用复杂(功能高级导致),需要手动释放锁,操作不当容易产生死锁
  • 因为是 JDK 提供,持续的优化力度可能没有 Synchronized 大

发表评论

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

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

相关阅读

    相关 Nginx 结构分析

    Nginx 源码基本结构 学习 Nginx 的构架之前,对 Nginx 源码结构进行简单的分析,可以了解 Nginx 模块结构以及模块之间的关系。充分理解Nginx 的