• 【ReentrantLock源码分析】1.xdb中的使用 2.获取和阻塞(阻塞前的一些死心不改)的源码


    1.xdb中的使用例子

    在xdb中,我们大概执行业务时的流程简化如下:

    1. package org.example.testReentrantLock;
    2. import java.util.concurrent.TimeUnit;
    3. import java.util.concurrent.locks.ReentrantLock;
    4. public class Main {
    5. public static final ReentrantLock lock = new ReentrantLock();
    6. public static void main(String[] args) throws InterruptedException {
    7. new Thread(() -> {
    8. try {
    9. lock.lock();
    10. // 这里相当于执行业务逻辑,在事务内再次持有一次锁,加入到自己本地的LockList中
    11. lock.lock();
    12. System.out.println("1111");
    13. TimeUnit.SECONDS.sleep(2);
    14. lock.unlock();
    15. } catch (InterruptedException e) {
    16. e.printStackTrace();
    17. } finally {
    18. lock.unlock();
    19. }
    20. }, "a").start();
    21. TimeUnit.SECONDS.sleep(1);
    22. new Thread(() -> {
    23. try {
    24. lock.lock();
    25. System.out.println("2222");
    26. } finally {
    27. lock.unlock();
    28. }
    29. }, "b").start();
    30. }
    31. }
    32. /*
    33. 1111
    34. 2222
    35. */

    思考:为啥我们用ReentrankLock而不是synchronized呢?因为:我们需要提供可重入的特性,而且释放是要控制在:事务完成后(不管是回滚了还是提交成功了)才释放锁,因此要使用这种显式锁。

    2.源码分析

    step1

    public static final ReentrantLock lock = new ReentrantLock();

    实现如下:

    1. public ReentrantLock() {
    2. sync = new NonfairSync();
    3. }

    可以看出来,默认情况下,是非公平锁,想想目前我还没有遇到要用公平锁的情况。

    step2

    接下来是上锁:

    lock.lock();
    1. /**
    2. * Acquires the lock.
    3. 获取锁
    4. *
    5. *

      Acquires the lock if it is not held by another thread and returns

    6. * immediately, setting the lock hold count to one.
    7. 如果这个锁没有被其它线程持有,那么会立即获取锁,并且设置锁的持有次数为1
    8. *
    9. *

      If the current thread already holds the lock then the hold

    10. * count is incremented by one and the method returns immediately.
    11. 如果当前线程已经持有锁,那么就会增加锁的持有数量,然后立即返回
    12. *
    13. *

      If the lock is held by another thread then the

    14. * current thread becomes disabled for thread scheduling
    15. * purposes and lies dormant until the lock has been acquired,
    16. * at which time the lock hold count is set to one.
    17. 如果此锁被其它线程持有了,那么此线程立即变成不可用然后进入睡眠状态,直到锁被获取到
    18. 那时,锁的持有数量被设置为1
    19. */
    20. public void lock() {
    21. sync.acquire(1);
    22. }

    进入到acquire里面:// && 符号的使用,前面为true才会走后门

    1. public final void acquire(int arg) {
    2. if (!tryAcquire(arg) &&
    3. // 这个很重要,如果没有获取到,也就是返回了false,那么肯定要检查阻塞
    4. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    5. selfInterrupt();
    6. }

    这里走非公平下的获取:

    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. }

    再次进入看下非公平模式下如何获取锁的:

    1. abstract static class Sync extends AbstractQueuedSynchronizer {
    2. private static final long serialVersionUID = -5179523762034025860L;
    3. /**
    4. * Performs non-fair tryLock. tryAcquire is implemented in
    5. * subclasses, but both need nonfair try for trylock method.
    6. */
    7. @ReservedStackAccess
    8. final boolean nonfairTryAcquire(int acquires) {
    9. final Thread current = Thread.currentThread();
    10. // 查看下当前的状态,这个state是加了volatile的,volatile int state;
    11. int c = getState();
    12. // 根据此状态位判断出来,如果没有被其它线程持有
    13. if (c == 0) {
    14. // 使用cas,如果当前是0,则设置为1
    15. if (compareAndSetState(0, acquires)) {
    16. // 设置当前线程占有了
    17. setExclusiveOwnerThread(current);
    18. return true;
    19. }
    20. }
    21. // 如果不是0,而且就是这个线程持有的
    22. else if (current == getExclusiveOwnerThread()) {
    23. // 计数+1
    24. int nextc = c + acquires;
    25. if (nextc < 0) // overflow
    26. throw new Error("Maximum lock count exceeded");
    27. // 设置下最新的计数
    28. setState(nextc);
    29. return true;
    30. }
    31. // 走到这,说明其它线程已经持有了(state既不是0,也不是自己占有)
    32. return false;
    33. }

    既然没有获取到,那么肯定就要阻塞自己了

          acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

    如何实现阻塞的呢?进去  // 可以发现aqs在没有获取到的时候,其实是先cas死心不改尝试获取下

    1. /*
    2. * Various flavors of acquire, varying in exclusive/shared and
    3. * control modes. Each is mostly the same, but annoyingly
    4. * different. Only a little bit of factoring is possible due to
    5. * interactions of exception mechanics (including ensuring that we
    6. * cancel if tryAcquire throws exception) and other control, at
    7. * least not without hurting performance too much.
    8. */
    9. /**
    10. * Acquires in exclusive uninterruptible mode for thread already in
    11. * queue. Used by condition wait methods as well as acquire.
    12. *
    13. * @param node the node
    14. * @param arg the acquire argument
    15. * @return {@code true} if interrupted while waiting
    16. */
    17. final boolean acquireQueued(final Node node, int arg) {
    18. boolean interrupted = false;
    19. try {
    20. for (;;) {
    21. // 可以看出来这里是aqs相对于synchronized的优化,没获取到暂时不死心
    22. // 这里依然再走一下尝试获取
    23. final Node p = node.predecessor();
    24. if (p == head && tryAcquire(arg)) {
    25. // 走这里,说明还真获取到了
    26. setHead(node);
    27. p.next = null; // help GC
    28. return interrupted;
    29. }
    30. // 走到这说明真是获取不到了,那么应该是进入阻塞状态了
    31. if (shouldParkAfterFailedAcquire(p, node))
    32. // 这里可以看出来,是调用的park实现的阻塞
    33. interrupted |= parkAndCheckInterrupt();
    34. }
    35. } catch (Throwable t) {
    36. cancelAcquire(node);
    37. if (interrupted)
    38. selfInterrupt();
    39. throw t;
    40. }
    41. }

    真的没办法了,阻塞自己呗

    1. private final boolean parkAndCheckInterrupt() {
    2. LockSupport.park(this);
    3. return Thread.interrupted();
    4. }

    park

    1. public static void park(Object blocker) {
    2. // 要被阻塞的线程
    3. Thread t = Thread.currentThread();
    4. setBlocker(t, blocker);
    5. // 这里是真的阻塞了,是一个native方法
    6. U.park(false, 0L);
    7. setBlocker(t, null);
    8. }

  • 相关阅读:
    vue的函数式组件
    B-Tree 索引和 Hash 索引的对比
    nodejs的express负载均衡
    湖仓一体电商项目(十):业务实现之编写写入DWD层业务代码
    【毕业设计项目】基于单片机的手势识别设计与实现 - 物联网 嵌入式 stm32 c51
    GoJS 使用笔记
    第n个程序员节
    如何使用Apple Watch解锁iPhone和Mac?
    C++语言之组合、聚合类间关系
    gitKraken安装于基本使用
  • 原文地址:https://blog.csdn.net/themagickeyjianan/article/details/128159473