02_AbstactQueuedSynchronizer之ReentantLock

简介

Lock接口的实现类,基本都是通过【聚合】了一个【队列同步器】的子类完成线程访问控制的。

public class ReentrantLock implements Lock, java.io.Serializable { //Lock接口的实现类
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     * 【聚合】了一个【队列同步器】(AbstractQueuedSynchronizer)的子类(Sync)
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

ReentrantLock的原理

在这里插入图片描述

从简单的lock方法开始看看公平和非公平

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以明显看出公平锁与非公平锁的lock()方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件:

hasQueuedPredecessors();hasQueuedPredecessors是公平锁加锁时判断等待队列中是否存在有效节点的方法。

public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
}

非公平方法lock()

对比公平锁和非公平锁的tryAcquire()方法的实现代码,其实差别就在于非公平获取锁时比公平锁中少了一个判断

!hasQueuedPredecessors(); 该方法中判断了是否需要排队,导致公平锁和非公平锁的差异如下:

公平锁: 公平锁讲究先来先到,线程在获取锁时,如果这个锁的等待队列中已经有线程在等待,那么当前线程就会进入等待队列中;

非公平锁: 不管是否有等待队列,如果可以获取锁,则立刻占有锁对象。也就是说队列的第一个排队线程在unpark(),之后还是需要竞争锁(存在线程竞争的情况下)

在这里插入图片描述

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}

整个ReentrantLock的加锁过程,可以分为三个阶段:

  1. 尝试加锁;
  2. 加锁失败,线程入队列;
  3. 线程入队列后,进入阻塞状态。

对应下面①②③三部分。
在这里插入图片描述

lock方法

在这里插入图片描述

acquire方法

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
    * Performs lock.  Try immediate barge, backing up to normal
    * acquire on failure.
    */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());//设置独占线程为当前线程
        else
            acquire(1);//线程B进入acquire方法
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
//acquire方法:java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
//继续跟踪tryAcquire方法java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquire
//父类定义,抛出异常要求子类必须实现该方法;ctrl+alt+b找实现类
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
//继续跟踪java.util.concurrent.locks.ReentrantLock.NonfairSync#tryAcquire
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
//继续跟踪到nonfairTryAcquire方法;如下:

目前的执行状态:
在这里插入图片描述

ThreadA在执行,state=1,ThreadB要进入队列排队。

tryAcquire方法

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();//获取当前线程
    int c = getState();//ThreadA已经占用 1 
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }  
    else if (current == getExclusiveOwnerThread()) { //进入else if
        //current 当前线程ThreadB,getExclusiveOwnerThread()是线程ThreadA
        //如果当前线程是线程拥有者,也就是true;表示是可重入锁,继续让当前线程执行
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;//返回false
}

再回到tryAcquire方法

//上面方法返回false,取反之后是true
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

如果tryAcquire(arg)返回false,取反之后返回true;则继续执行&&后面的方法 addWaiter方法。

addWaiter方法

//java.util.concurrent.locks.AbstractQueuedSynchronizer#addWaiter
private Node addWaiter(Node mode) {
    //当前线程是ThreadB
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;//tail默认值是null;
    if (pred != null) { //null != null false;if不执行
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);//进入该方法
    return node;
}

入队方法:

/**
 * Inserts node into queue, initializing if necessary. See picture above.
 * @param node the node to insert
 * @return node's predecessor
 */
private Node enq(final Node node) {
    for (;;) {  //第一次自旋 完成初始化
        Node t = tail;// t = null
        if (t == null) { // Must initialize 必须初始化,说明没有任何一个元素入队
            if (compareAndSetHead(new Node()))//比较并设置头节点,设置新建一个空节点作为占位
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
//继续跟踪compareAndSetHead方法
/**
 * CAS head field. Used only by enq.
 */
private final boolean compareAndSetHead(Node update) {
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

如图所示:
在这里插入图片描述

head头节点指向一个new Node() 的空接点。注意:for循环是死循环,继续执行for循环:

双向链表中,第一个节点为虚节点也叫哨兵节点,其实并不存储任何信息,只是占位。真正的第一个有数据的节点,是从第二个节点开始的。

private Node enq(final Node node) {
    for (;;) {  //第一次自旋完成初始化
        Node t = tail;// t = null
        if (t == null) { // Must initialize 必须初始化,说明没有任何一个元素入队
            if (compareAndSetHead(new Node()))//比较并设置头节点,设置新建一个空节点作为占位
                tail = head;
        } else { //第二次自旋完成入队执行else分支
            node.prev = t;//node是线程B,B节点的前指针指向尾节点
            if (compareAndSetTail(t, node)) { //比较并设置尾节点为ThreadB节点
                t.next = node;//哨兵节点的next指向ThreadB节点
                return t;//返回t节点,头节点
            }
        }
    }
}

在这里插入图片描述

第二次循环之后:

在这里插入图片描述

ThreadC线程执行,也是执行addWaiter方法

/**
 * Creates and enqueues node for current thread and given mode.
 *
 * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
 * @return the new node
 */
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail; //C节点进入,
    if (pred != null) { //尾指针不为null,进入if结构
        node.prev = pred; //把C节点的上一个节点指向pred--->tail尾节点
        if (compareAndSetTail(pred, node)) { //比较并设置尾指针为节点C
            pred.next = node;//把节点C连接到尾指针
            return node;
        }
    }
    enq(node);
    return node;
}

如下图所示:
在这里插入图片描述

小总结:
在这里插入图片描述

acquireQueued(addWaiter(Node.EXCULUSIVE),args)方法

继续跟踪方法:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

这次跟踪acquireQueued方法:

/**
 * Acquires in exclusive uninterruptible mode for thread already in
 * queue. Used by condition wait methods as well as acquire.
 *
 * @param node the node
 * @param arg the acquire argument
 * @return {@code true} if interrupted while waiting
 */
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; 
    try {
        boolean interrupted = false;//是否被打断
        for (;;) {//自旋
            final Node p = node.predecessor();//获取上一个节点:哨兵节点
            if (p == head && tryAcquire(arg)) { //p == head是true,tryAcquire再次尝试失败
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //抢占失败应该park
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)  //如果failed为true则取消入队
            cancelAcquire(node);
    }
}

继续跟踪shouldParkAfterFailedAcquire方法:

/**
 * Checks and updates status for a node that failed to acquire.
 * Returns true if thread should block. This is the main signal
 * control in all acquire loops.  Requires that pred == node.prev.
 *
 * @param pred node's predecessor holding status
 * @param node the node
 * @return {@code true} if thread should block
 */
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;//哨兵节点的waitStatus=0,获取前驱节点的状态
    if (ws == Node.SIGNAL) // -1 不执行; 如果是SIGNAL状态,即等待被占用的资源释放,直接返回true
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) { //ws大于0说明是CANCELLED状态
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//把哨兵节点的waitStatus由0变为-1
    }
    return false;
}

在这里插入图片描述
再跟踪acquireQueued的for循环,因为是自旋:

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; 
    try {
        boolean interrupted = false;//是否被打断
        for (;;) {//自旋
            final Node p = node.predecessor();//获取上一个节点:哨兵节点
            if (p == head && tryAcquire(arg)) { //p == head是true,tryAcquire再次尝试失败
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //抢占失败应该park
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)  //如果failed为true则取消入队
            cancelAcquire(node);
    }
}

再次执行shouldParkAfterFailedAcquire方法:

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus; //第二次自旋 -1 
    if (ws == Node.SIGNAL) //true
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true; //返回true
    if (ws > 0) {
    ....

继续跟踪acquireQueued方法:

//抢占失败应该park
if (shouldParkAfterFailedAcquire(p, node) &&
    parkAndCheckInterrupt())
    interrupted = true;

shouldParkAfterFailedAcquire返回true之后执行&&后面的方法parkAndCheckInterrupt;

如果前驱节点的waitStatus是SIGNAL状态,即shouldParkAfterFailedAcquire方法会返回true程序会继续向下执行parkAndCheckInterrupt方法,用于将当前线程挂起。

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);//ThreadB在此一直等待,线程挂起,程序不会继续向下执行
    //根据park方法api描述,程序在下述三种情况会继续向下执行
    //1. 被unpark
    //2. 被中断(interrupt)
    //3. 其他不和逻辑的返回才会继续向下执行
    //因上述三种情况程序执行至此,返回当前线程的中断状态,并清空中断状态,
    //如果由于被中断,该方法会返回true
    return Thread.interrupted();
}

当Node里的waitStatus等于-1时阻塞线程等待重新唤醒。也就是等待ThreadA执行unPark方法之后ThreadB才能上位执行。

小总结:执行到acquireQueued方法之后,ThreadB和ThreadC两个线程才算真正的入队完成,进入阻塞状态。

unlock方法

方法实现:

public void unlock() {
    sync.release(1);
}

继续跟踪方法:

//java.util.concurrent.locks.AbstractQueuedSynchronizer#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;
}

继续跟踪方法:

protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
//java.util.concurrent.locks.ReentrantLock.Sync#tryRelease
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;//1 - 1 
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {   //执行if结构
        free = true;
        setExclusiveOwnerThread(null);//设置当前窗口的线程为null
    }
    setState(c);//设置state = 0;
    return free;
}

在这里插入图片描述
在回到release方法

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0) //h不为null, waitStatus也不为0;进入if结构
            unparkSuccessor(h);//执行该方法
        return true;
    }
    return false;
}

跟踪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)
        compareAndSetWaitStatus(node, ws, 0);//把waitStatus设置为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.
     */
    Node s = node.next;//哨兵节点的下一个节点ThreadB
    if (s == null || s.waitStatus > 0) { //ThreadB的waitStatus不大于0,if块不进入
        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);//解锁线程B
}

在这里插入图片描述

以上流程的代码:

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);//ThreadB线程出队
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&  //抢占失败后应该park
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

ThreadB线程出队过程:

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值