并发编程-ReentrantLock加锁/解锁源码分析

1、ReentrantLock是AQS(AbstractQueuedSyhronizer)在java层面的一种具体实现

 =============================以下主要为thread0加锁,thread1、thread2创建节点并入队阻塞逻辑==========================

1.1、下面使用以下代码分析多个线程获取锁的原理

    private static  int sum = 0;
    private static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(()->{
                lock.lock();
                try{
                    // 临界区代码
                    // TODO 业务逻辑:读写操作不能保证线程安全
                    for (int j = 0; j < 10000; j++) {
                        sum++;
                    }
                }finally {
                    lock.unlock();
                }
            });
            thread.start();
        }
        Thread.sleep(2000);
        System.out.println(sum);
    }

1.2、第一个线程thread0通过设置state的值为1获取到非公平锁,设置当前线程为拥有独占访问权限的线程,具体实现如下:

//非公平锁
static final class NonfairSync extends Sync {
        final void lock() {
         /**
          * 通过cas获取锁成功
          * 在这里就是thread0获取锁成功
          */
            if (compareAndSetState(0, 1))
                //设置当前线程为拥有独占访问权限的线程
                setExclusiveOwnerThread(Thread.currentThread());
            else
        /**
         * 通过cas获取锁成功
         * 1、再通过cas尝试加一次锁
         * 2、加锁失败,为当前线程和给定模式(SHARED,EXCLUSIVE)创建节点并使其入队。    
         */
                acquire(1);
        }
}

1.2、************************当thread0正在执行业务逻辑并且未释放锁之前*****************************

thread1线程开始通过cas获取锁,此时会获取锁失败,并把当前线程和给定模式(SHARED,EXCLUSIVE)创建节点放入队列中,并阻塞当前线程

//非公平锁
static final class NonfairSync extends Sync {
        final void lock() {
            if (compareAndSetState(0, 1))
            ...
            else
        /**
         * 通过cas获取锁成功
         * 1、再通过cas尝试加一次锁
         * 2、加锁失败,为当前线程和给定模式(SHARED,EXCLUSIVE)创建节点并使其入队。    
         * 此时thread1获取锁失败,执行创建节点并入队操作
         */
                acquire(1);
        }
}

1.2.1、thread1具体入队执行逻辑,并阻塞线程逻辑如下:

 private Node addWaiter(Node mode) {
    //根据线程和模式(SHARED,EXCLUSIVE)创建节点
        Node node = new Node(Thread.currentThread(), mode);
        //第一次创建队列队列的tail为null
    Node pred = tail;
    //当队列存在的时候,其他线程入队执行的逻辑
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        /**
          * 首次创建队列并入队阻塞的逻辑
          * 此时enq(node)就是thread1执行的入队逻辑
          */
        enq(node);
        return node;
}

1.2.2、thread1入队之后的阻塞逻辑

/** 
  * tryAcquire为加锁逻辑
  * addWaiter为创建节点并入队逻辑
  * acquireQueued为阻塞逻辑
  */
  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
  }
   /**
     * 以独占不间断模式为已在中的线程获取队列。由条件等待方法和获取方法使用
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            // 返回上一个节点,如果为null则抛出NullPointerException。前置任务不能为null时使用。空检查可以被忽略
                final Node p = node.predecessor();
        // 通过cas加锁,加锁成功把队列的head指向当前节点,把之前节点的指针置为null
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // GC回收之前的head节点
                    failed = false;
                    return interrupted;
                }
        /* 
         * shouldParkAfterFailedAcquire(p, node)修改节点等待状态为-1(Node.waitStatus=Node.SIGNAL)
         * parkAndCheckInterrupt()为阻塞逻辑,具体通过调用LockSupport.park(this),当前线程进入WAIT(无时限等待状态)
         */
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

 

2、thread2线程开始通过cas获取锁,此时会获取锁失败,并把当前线程和给定模式(SHARED,EXCLUSIVE)创建节点放入队列中,并阻塞当前线程

与thread1不同的地方如下:

   private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
    /**
      * thread2入队逻辑,并把当前节点的插入到队列的队尾
          */
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        ...
        return node;
    }

 =============================以下主要为thread0释放锁,唤醒thread1加锁逻辑==========================

 3、thread0释放锁逻辑如下
public final boolean release(int arg) {
       /**
         * thread0线程释放锁逻辑
     * 1、修改state状态为0
     * 2、把当前拥有独占访问权限的线程置空
         */
    if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
            //thread0线程通过LockSupport.unpark(s.thread)唤醒thread1线程逻辑
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

4、thread0唤醒thread1线程具体逻辑如下

    /**
      * 唤醒节点的后续节点(如果存在)。
      * @param node节点
      */
    private void unparkSuccessor(Node node) {
    //cas修改当前节点状态state为0
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        //获取将要被唤醒的队列里的节点
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
    //当被唤醒的线程不为空时,执行唤醒
        if (s != null)
            LockSupport.unpark(s.thread);
    }

5、thread1被唤醒后执行的代码逻辑

/**
     * 以独占不间断模式为已在中的线程获取队列。由条件等待方法和获取方法使用
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            // 返回上一个节点,如果为null则抛出NullPointerException。前置任务不能为null时使用。空检查可以被忽略
                final Node p = node.predecessor();
                // ==========thread1线程被唤醒,通过cas加锁,加锁成功把队列的head指向当前节点,把之前节点的指针置为null=========
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // GC回收之前的head节点
                    failed = false;
                    return interrupted;
                }
        /* 
         * shouldParkAfterFailedAcquire(p, node)修改节点等待状态为-1(Node.waitStatus=Node.SIGNAL)
         * parkAndCheckInterrupt()为阻塞逻辑,具体通过调用LockSupport.park(this),当前线程进入WAIT(无时限等待状态)
*/ if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }

 

posted @ 2023-06-04 12:08  西风51668  阅读(132)  评论(0)    收藏  举报