java并发锁ReentrantLock源码分析一 可重入支持中断锁的实现原理

本文详细分析了Java并发锁ReentrantLock的源码,包括如何通过自旋加入队列、获取锁的流程、释放锁的机制以及中断支持。文章解释了节点加入队列、shouldParkAfterFailedAcquire和parkAndCheckInterrupt等关键方法的作用,阐述了从尝试获取锁到阻塞等待,再到唤醒和重新尝试获取锁的完整过程。
摘要由CSDN通过智能技术生成
  • 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;

if (t == null) { // Must initialize @1

if (compareAndSetHead(new Node()))

tail = head;

} else {

node.prev = t;

if (compareAndSetTail(t, node)) {

t.next = node;

return t;

}

}

}

}

使用自旋来加入,众所周知,CLH算法,需要初始化一个假的 head 节点,也就是 head 节点并不代表一个等待获取锁的对象,AbstractQueuedSynchronzier 选择初始化 head,tail 的时机为第一次产生锁争用的时候。@1处为初始化head,tail,设置成功后,初始化后,再将新添加的节点放入到队列的尾部,然后该方法会返回原先的尾节点。addWaiter方法执行后,继续回到acquire(args)方法处:

public final void acquire(int arg) {

if (!tryAcquire(arg) &&

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

selfInterrupt();

}

接下来,查看 acquireQueued 方法,addWaiter 方法返回的是代表当前线程的 Node 节点。

/**

  • 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(); // @1

if (p == head && tryAcquire(arg)) { // @2

setHead(node);

p.next = null; // help GC

failed = false;

return interrupted;

}

if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt() ) //@3

interrupted = true;

}

} finally {

if (failed)

cancelAcquire(node);

}

}

首先@1,获取该节点的 node 的上一个节点。

@2如果node的前节点是head,因为head初始化时,都是假节点,不代表有线程拥有锁,所以,再次尝试获取锁,如果获取锁,则将锁的 head 设置为当前获取锁的线程的 Node,然后返回 false。返回 false, 则代表 if (!tryAcquire(arg) &&  acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 的结果为 false,直接返回,并不需要设置中断标记。如果当前节点不是head的话,则说明该锁被别的线程占用了,那就需要等待其他线程释放该锁,具体,我们看一下shouldParkAfterFailedAcquire,为了更好的理解 shouldParkAfterFailedAcquire, 我们先看一下parkAndCheckInterrupt 方法。

/**

  • Convenience method to park and then check if interrupted

  • 阻塞该线程,然等待唤醒后,会返回 当前线程的中断位;

  • @return {@code true} if interrupted

*/

private final boolean parkAndCheckInterrupt() {

LockSupport.park(this);

return Thread.interrupted();

}

/**

  • 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

该方法,如果返回true,则代表该线程将被阻塞。

*/

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

int ws = pred.waitStatus; // @1

if (ws == Node.SIGNAL) // @2

/*

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

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值