ReentrantLock部分源码分析

90 阅读3分钟

ReentrantLock底层有AQS和CAS的原理,要先了解下

AQS

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。

如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

原理图

image.png

CAS

CAS(compare and swap),比较并交换。可以解决多线程并行情况下使用锁造成性能损耗的一种机制.CAS 操作包含三个操作数—内存位置(V)、预期原值(A)和新值(B)。

如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。一个线程从主内存中得到num值,并对num进行操作,写入值的时候,线程会把第一次取到的num值和主内存中num值进行比较,如果相等,就会将改变后的num写入主内存,如果不相等,则一直循环对比,知道成功为止。

我的理解:往往我们操作一个参数的时候,比如说i=0,在进行i++的时候,是要先将i的值取出来,然后+1,最后才刷新会内存(这里会涉及到JAVA的内存模型),这其实就是三个指令,如果我现在只执行了第一条指令就cpu时间片到了,挂起来了,当再次获取到时间片的时候,这个时候i还是0吗,还是原来的0(ABA问题)吗???

所以说CAS就可以将我们三条指令变成一个原子性的操作,当然可以加入volatile关键字来确保顺序性和可见性

CAS问题:

  • ABA问题:加入版本号,可以利用AtomicStampReference的版本号
    
  • 只能对一个参数进行原子性操作问题
    
  • 自旋的时间问题
    

ReentrantLock

所有的方法我都是在JDK11上查看的,可能其他版本会有所不同

lock()方法,获取锁

就是执行sync.acquire(1)方法

acquire()方法

主要是执行tryAcquire(有公平锁和非公平锁锁的区别),addWaiteracquireQueued这三个方法

//尝试获取锁
public final void acquire(int arg) {
    //尝试获取锁
    if (!tryAcquire(arg) &&
        //addWaiter方法:如果获取锁不成功就将当前线程加入等待队列中,但此时线程还在运行
        //acquireQueued方法: 继续尝试获取锁
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire()方法,非公平锁

//非公平锁执行tryAcquire会进入下面这个方法
final boolean nonfairTryAcquire(int acquires) {
    //拿到当前线程
    final Thread current = Thread.currentThread();
    int c = getState();
    //state为0表示没有线程持有锁
    //state大于0就代表有线程持有锁
    //大于1表示加锁线程重入了
    if (c == 0) {
        //CAS比较并交换,设置state为1
        if (compareAndSetState(0, acquires)) {
            //设置加锁线程为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //判断加锁线程是不是当前线程,是就将state++
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

tryAcquire(),此时是公平锁

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        //与非公平锁对比就多了这样这行代码
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
hasQueuedPredecessors()方法
public final boolean hasQueuedPredecessors() {
    Node h, s;
    //第一种情况:如果head为空,表示没有线程持有锁,就直接返回false,调用方会取反
    //第二种情况: 如果head为空, 表示可能有线程持有锁,因为第一次获取锁不会创建head,但即使返回false也会去尝试获取锁,
    //第三种情况:如果head不为空,表示可能有线程持有锁(即使线程释放锁,在没有其他线程获取锁的情况下,head还是指向原来的node节点,不会变为null)
    if ((h = head) != null) {
        //
        if ((s = h.next) == null || s.waitStatus > 0) {
            s = null; // traverse in case of concurrent cancellation
            //从尾节点开始往前找,找第一个waitStatus<0(已经挂起的)线程
            for (Node p = tail; p != h && p != null; p = p.prev) {
                if (p.waitStatus <= 0)
                    s = p;
            }
        }
        //判断节点是不是自己,如果不是就返回true,调用方会取反
        if (s != null && s.thread != Thread.currentThread())
            return true;
    }
    return false;
}

addWaiter()方法

private Node addWaiter(Node mode) {
    Node node = new Node(mode);

    for (;;) {
        Node oldTail = tail;
        if (oldTail != null) {
           //设置当前节点的前驱为曾经的尾节点,
           //debug会发现方法还将oldTail和tail变得不一样了,也就导致第一次for循环下面的if语句不成立
            node.setPrevRelaxed(oldTail);
            //CAS比较并交换将,将当前tail设置为当前节点
            if (compareAndSetTail(oldTail, node)) {
                //将以前的尾指针的后继节点设置为当前节点
                oldTail.next = node;
                return node;
            }
        } else {
            //当前线程未能加锁成功,且head未初始化,将new一个node节点赋值给head和tail
            //第一个线程尝试加锁不会走到这,因为他直接加锁成功了
            initializeSyncQueue();
        }
    }
}

acquireQueued()

final boolean acquireQueued(final Node node, int arg) {
    boolean interrupted = false;
    try {
        for (;;) {
            //拿到当前节点的前驱节点
            final Node p = node.predecessor();
            //head:可以表示加锁的那个线程,也可以表示才释放锁的线程
            //如果前驱节点等于head,那么可能已经释放锁了,执行tryAcquire方法去尝试获取锁
            if (p == head && tryAcquire(arg)) {
                //设置head为当前节点
                setHead(node);
                //这是原head节点已经处理完了,让他的引用结束
                p.next = null; // help GC
                return interrupted;
            }
            //第一次循环shouldParkAfterFailedAcquire方法将waitStatus状态设置为-1
            //第二次循环shouldParkAfterFailedAcquire方法将返回true,然后将当前线程挂起
            if (shouldParkAfterFailedAcquire(p, node))
                interrupted |= parkAndCheckInterrupt();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        if (interrupted)
            selfInterrupt();
        throw t;
    }
}

trylock(),尝试获取锁

就是执行nonfairTryAcquire()方法,前面也讲过了

unlock(), 释放锁

执行unlock()方法,会调用sync.release(1);方法

release()方法

public final boolean release(int arg) {
   //尝试释放锁
   if (tryRelease(arg)) {
       Node h = head;
       if (h != null && h.waitStatus != 0)
           //唤醒后面节点去获取锁
           unparkSuccessor(h);
       return true;
   }
   return false;
}

tryRelease()方法

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //等于0表示都已经解锁完成了
    if (c == 0) {
        free = true;
        //最后将加锁线程设置为null
        setExclusiveOwnerThread(null);
    }
    setState(c);
    //非公平锁的情况,到这就代表可能会被其他线程获取到锁了
    return free;
}

unparkSuccessor()方法

private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        node.compareAndSetWaitStatus(ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    //这里是唤醒head节点后面的节点
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        //从尾节点往前找第一个被挂起的线程
        for (Node p = tail; p != node && p != null; p = p.prev)
            if (p.waitStatus <= 0)
                s = p;
    }
    if (s != null)
        //唤醒s这个线程
        LockSupport.unpark(s.thread);
}

按照自己的理解写的,望各位大佬体谅,欢迎指正。