源码-RentrantLock

构造函数

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {// 默认使用非公平锁
    sync = new NonfairSync();
}

lock方法

 public void lock() {// 默认使用非公平锁的lock()方法
    sync.lock();
 }
/**
 * Performs lock.  Try immediate barge, backing up to normal
 * acquire on failure.
 */
final void lock() {// 非公平锁的方法
    //设置state为1(如果同一个线程多次进入,就会一直加1(重入锁))
    if (compareAndSetState(0, 1))
        // 设置为当前线程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);//CAS失败后执行
}

获取锁

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&  //nonfairTryAcquire()
        // cas失败,追加到tail,head->node1->node2->...
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

非公平获取锁

/**
 * Performs non-fair tryLock.  tryAcquire is implemented in
 * subclasses, but both need nonfair try for trylock method.
 */
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {//如果之前的线程释放了锁,state为0
        //继续CAS(内存中的实际值和期望值一致才能cas成功)
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //重入
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;//state为2
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

获取锁、入队列排队

/* 谁获取锁谁就将自己设置为头节点,之前获取锁的被舍去回收当前线程获取

锁后,会将自己设置为头,后面入队的会将当前线程的状态设置为-1(因为获取

不到锁),所以在释放锁的时候会判断是否头节点状态不等于0再唤醒头节点后面

的一个节点(导致这种现象是因为获取锁的线程很快执行完就要去释放锁了,而在

队列的节点还没有来的及将前节点状态置为-1)  
                                 
1、在锁第一次被获取时,第一个获取锁的线程不会入队阻塞,第二个线程因第一

个线程获取锁而准备入队阻塞,首先new一个内容为空的节点,然后再new一个包

含当前线程的节点,挂在空节点后面,这两个节点状态都为0,当第一个线程准备

释放锁的时候,如果头节点状态为0,说明第二个线程当前还没有阻塞(因为阻塞

的过程是:先将前节点状态设置为-1,然后再次尝试获取锁失败后,然后阻塞),

换句话说,第二个节点还没有执行到shouldParkAfterFailedAcquire方法的最

后一步(将前节点状态设置为-1),还有机会去获取锁,所以第一个线程就不调用

unparkSuccessor方唤醒头节点后面的节点了。*/
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 当前节点前一个节点
            final Node p = node.predecessor();
            // 只有前节点是头节点才去获取锁(state+1,thread)
            if (p == head && tryAcquire(arg)) {
                // 当前节点设置为头节点
                setHead(node);
                // 之前头节点被GC(之前头节点next置空,指向他的当前节点的prev也置空)
                p.next = null; // help GC,
                failed = false;
                return interrupted;
            }
            // 只有当前节点前一节点的状态为NODE.SIGNAL状态,才park
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)// 挂起后被打断,取消获取
            cancelAcquire(node);
    }
}
 /**
     * 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;//第一次为null
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);//第一次走(多个线程进入)
        return node;
    }
private Node enq(final Node node) {
    for (;;) {// 自旋
        Node t = tail;//第一次为null
        if (t == null) { // Must initialize
            //创建一个节点(多个线程进入,第一个线程执行完以后,其他线程得知t!=null)
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 当前节点头部先指向尾部
            node.prev = t;
            // 当前节点设置为尾节点(将原来tail节点指向当前节点的地址)
            if (compareAndSetTail(t, node)) {
                // 之前尾部节点指向当前节点
                t.next = node;
                return t;
            }
        }
    }
}
// 1、如果前节点状态为SIGNAL,直接返回true,然后阻塞
// 2、如果前节点状态为无效,重组队列,继续循环
// 3、如果前节点状态为0(刚加入队列的节点),将其状态cas为SIGNAL,继续循环
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {//取消状态
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 前节点的等待状态为0(条件队列入队时是0,满足这个条件,后面再入队的将其设为Node.SIGNAL)
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

被中断线程执行

private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    if (node == null)
        return;

    node.thread = null;

    // Skip cancelled predecessors
    Node pred = node.prev;
    while (pred.waitStatus > 0) // 寻找不是取消状态的节点
        node.prev = pred = pred.prev;

    Node predNext = pred.next;// 不是取消状态的节点的下一节点

    // 一般来说有三种线程(线程和节点为同等关系,线程绑定了节点)
    // 1、一个获取锁的正在执行的线程,
    // 2、还有当前被中断正在执行这段代码的线程
    // 3、其他正在排队的线程
    
    // 将当前被中断的节点状态设置为取消,
    // 1、第一种线程在执行unparkSuccessor时(如果当前被中断的节点刚好在第一种线程后面),
    //那么就从尾节点开始遍历找到第一种线程后面第一个状态为SIGNAL的节点唤醒
    // 2、第三种线程在执行shouldParkAfterFailedAcquire时候会重组队列,跳过取消状态的节点
    node.waitStatus = Node.CANCELLED; 

    // 如果当前节点为tail,将前节点设置为tail
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        int ws;
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
            // 这一步唤醒后面第一个状态为SIGNAL的节点,可能是为了让后面的节点去重组队列吧,跳过当前被打断的无效的节点
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

解锁

public void unlock() {
        sync.release(1);
    }
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
// 唤醒后面第一个状态为SIGNAL的节点
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;// 头节点的状态
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;
    // 出现下面的情况是cancelAcquire导致的,某个节点被中断
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 找到不是取消状态的节点并unpark
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值