欢迎光临散文网 会员登陆 & 注册

软件测试之python自动化测试5(web/app/接口自动化/自动化框架)57期

2022-11-24 19:33 作者:山观那恭喜囧昂贵的  | 我要投稿


可重入锁获取和释放锁的过程分析
目的

软件测试之python自动化测试5(web/app/接口自动化/自动化框架)57期

download:https://www.zxit666.com/5679/

了解ReentrantLock获取和释放锁的过程。获取锁定进程
整个过程可以总结为做两件事。

成功获取锁,在当前线程中执行其他事情;
锁获取失败,当前线程加入同步队列,同时阻塞当前线程。

当第一个线程(thead0)进来时,通过CAS将state属性更改为1。如果成功,通过setExclusiveOwnerThread()方法将exclusiveOwnerThread设置为当前线程。

此时,当第二个线程(thead1)进来并通过CAS将state属性更改为1时,它将失败。这时,它进入acquire()方法。最终会得出以下方法:首先通过tryAcquire()方法再次尝试获取锁。

tryAcquire()方法仍然通过CAS获得锁。此时,锁资源仍由第二个线程持有,因此它将返回false。现在看看acquire()方法中的if判断。

此时,获取排队(添加服务员(节点。exclusive)、arg)进行判断。这里需要执行两个方法addWaiter()和acquireQueued()。首先看addWaiter()方法。这种方法,我们需要注意以下四点。



首先会构造一个node节点(节点内部细节见其构造方法)。
如果tail(同步队列尾节点指针)不为空,即同步队列不为空,那么步骤1中构造的节点将通过尾插入添加到队列中,然后返回。
如果同步队列为空,那么执行enq()方法,它为我们做了两件事。


如果同步队列为空,则初始化队列。
初始化队列后,将节点node入队。



通过addWaiter()方法和enq()方法,我们还可以看到,AQS中的同步队列是通过一个双向链表来实现的。当一个节点入队和出队时,需要修改两个指针(prev和next)。
addWaiter()方法执行后,我们通过下图大致看一下此时同步队列中指向的节点。在这里,如果不太理解,可以回头看看enq()方法的执行流程。

执行addWaiter()方法后,它将在入队后返回到新节点。然后开始执行acquireQueued()方法。这个方法做了五件事。


获取当前节点的前任节点p。
如果P是头节点,再尝试获取锁,如果成功获取锁,就可以跳出循环了。
无法获取锁。由shouldParkAfterFailedAcquire()将waitSatus改为-1(为什么改为-1?原因可以在AQS的源代码中找到,后续获取锁的过程会遵循这个逻辑)



4.ParkandChekingInterrupt()方法会阻塞当前线程,同时可以返回当前线程的中断状态(Thread.interrupted()会清除中断标志位)。
经过上面的操作,我们可以知道线程1没有获得锁,被添加到同步队列中,并阻塞了它。总结起来就是四个字:“入队”“封杀”。此时,我们的同步队列也变成如下所示。执行后与上述addWaiter()方法相比,只是thread 1节点的前任节点,waitStaus设置为-1。
释放锁定过程
整个过程(仅考虑解锁成功)可以总结为三件事。

释放锁资源;
唤醒同步队列中头节点之后的节点对应的线程。
步骤2被唤醒的线程试图竞争锁。如果竞争成功,则更新同步队列(即头节点出列)。

当第一个线程(thread0)执行自己的业务流程时,它将释放锁。至此,我们来看一下解除锁的过程。调用unlock()方法可以释放锁。需要注意的是,为了避免死锁,这个方法的调用需要放在最后的代码块中。

当thread0释放锁时,它调用release()方法,该方法主要做两件事。

调用tryRelease()方法来释放锁。

在方法内部,状态被设置为0,exclusiveOwnerThread被setExclusiveOwnerThread()方法设置为null时,意味着此时锁资源被释放,没有线程持有锁资源。



如果第一步返回true,即锁被成功释放,那么开始第二步。第二步是首先获取同步队列的头节点,并检查其waitStatus属性。这里,让我们把刚刚获得锁的节点的情况放在同步队列中。此时,我们的head节点的waitStatus为-1,所以我们将进入unparksuccess()方法。



parksuccess()方法主要做以下三件事。注意第三步。根据我们当前的同步队列,LockSupport.unpark()方法将唤醒线程1。

当执行unparksuccess()方法中的LockSupport.unpark()方法时,线程1(thread1)将被唤醒。线程1被唤醒后,我们来看看线程1的执行情况。此时,线程1将继续执行acquireQueued()方法中的for循环(注意:这是一个无限循环)。执行顺序与锁获取相同,但当与锁获取不同时,步骤2中的锁获取将会成功(因为thread0已经释放了锁)。

成功获取锁后,当前同步队列中的头节点将出队。此时,同步队列中节点的情况如下。

至此,释放锁的逻辑完成。其实总结起来很简单。首先释放锁资源,然后唤醒同步队列中头节点的下一个节点对应的线程,最后更新同步队列(出列)。
摘要
以上是ReentrantLock获取和释放锁的一般过程。通过本文,读者对ReentrantLock的锁的获取和释放过程有了一个大致的了解。细心的读者可能会发现,在获取锁时,acquireQueued()方法中有一个cancelAcquire()方法的调用逻辑。

软件测试之python自动化测试5(web/app/接口自动化/自动化框架)57期的评论 (共 条)

分享到微博请遵守国家法律