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

  • 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

/*

  • This node has already set status asking a

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的互斥是通过`ReentrantLock`类来实现的,它的实现原理是基于AQS(AbstractQueuedSynchronizer)同步器的。AQS是一个用于实现同步器的框架,它提供了两种同步状态,分别是独占模式和共享模式。 当一个线程请求时,如果没有被其他线程占用,则该线程成功获取并进入独占模式,此时其他线程再去请求时就会被阻塞。如果此时有其他线程已经占用了,则当前线程会被加入到一个等待队列中,并且进入阻塞状态。 在`ReentrantLock`中,还提供了可重入特性,也就是说同一个线程可以多次获取同一个,而不会被阻塞。这个特性是通过一个计数器来实现的,每当一个线程获取时,计数器加1,释放时计数器减1,这样同一个线程可以多次获取而不会阻塞。 `ReentrantLock`的实现原理主要包括以下几个步骤: 1. 初始化:初始化一个AQS同步器,同时初始化一个等待队列和一个线程对象; 2. 获取:如果未被占用,则当前线程获取并进入独占模式;如果已经被占用,则当前线程加入到等待队列中,并且进入阻塞状态; 3. 释放:当前线程释放,并且计数器减1,同时唤醒等待队列中的一个线程; 4. 可中断获取:如果当前线程在等待的过程中被中断,则会抛出`InterruptedException`异常。 总之,`ReentrantLock`是Java中一种功能强大的互斥实现方式,它能够支持重入特性、公平和非公平、可中断获取等多种功能。在多线程编程中,使用`ReentrantLock`可以有效地避免线程竞争和死等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值