丹凤千字科普:ie不能创建对象怎么办(详细资料介绍)


丹凤千字科普:ie不能创建对象怎么办(详细资料介绍)  

关于ReentrantLock加锁解锁原理的文章发布后,引起了大家热烈的讨论。许多小伙伴都前来询问,平时我们直接使用Synchronized关键字就能实现加解锁,为什么还需要引入ReentrantLock呢?

为了解决小伙伴们的疑惑,我们来对两者进行一个简要的比较:

相同点:

两者都是“可重入锁”,即一个线程获取到锁对象后,可以继续获取同一个锁对象,锁对象的计数器会进行“+1”操作。

不同点:

ReentrantLock是基于API实现的,而Synchronized是依赖于JVM实现的。

ReentrantLock可以响应中断,而Synchronized不可以。

ReentrantLock可以指定是公平锁还是非公平锁,而Synchronized只能是非公平锁。

ReentrantLock的lock是同步非阻塞的,采用的是乐观并发策略;而Synchronized是同步阻塞的,使用的是悲观并发策略。

ReentrantLock通过Condition接口可以实现多路选择通知,而Synchronized通过wait()和notify()/notifyAll()方法实现等待/通知机制(单路通知)。

ReentrantLock在实际使用场景中还是有其独特优势的。今天我们就来聊一聊它的多路选择通知功能。

接下来我们通过实战来深入理解ReentrantLock的工作原理。为了更直观地展示其原理,我们设计一个加油站场景。

场景描述:

加油站为了吸引更多车主前来加油,提供了免费洗车服务。我们规定汽车必须按照“加油->洗车->驶离”的流程来加油,只有等前一辆车驶离后,下一辆车才能进来加油。

代码实现:

首先创建锁对象并生成三个Condition。

然后声明加油、清洗、驶离的方法,并规定加完油之后去洗车并驶离加油站。

最后我们模拟3辆车同时到达加油站的场景。

图解:

大家可以看到,上述案例都是围绕Condition来操作的。那么什么是Condition呢?Condition是一个接口,里面定义了线程等待和唤醒的方法。

代码中调用的lock.newCondition()实际调用的是Sync类中的newCondition方法,而ConditionObject就是Condition的实现类。

了解了ConditionObject的数据结构之后,我们从源码角度来图解一下ReentrantLock的等待/唤醒机制。

首先看await方法:

如果线程被中断,则清除中断标记并抛出异常。

接着将当前线程封装成node加入等待队列尾部。然后释放锁资源,并进入循环等待状态。如果节点不在同步队列中,则挂起当前线程。如果发生了中断,则根据中断情况处理。被唤醒后该节点一定会在AQS队列上。如果获取到了锁并且之前存在中断并且不是中断唤醒的线程,将中断模式设置为重新中断。清除条件队列中所有状态不为CONDITION的节点。如果中断模式不为0,则报告中断后的等待情况。

接下来查看addConditionWaiter方法:将当前线程封装成node加入等待队列尾部。如果尾节点为空,则将首尾节点都指向当前节点。如果尾节点不为空,则将尾节点的nextWaiter指向当前节点,并将当前节点置为尾节点。

unlinkCancelledWaiters方法用于将不处于等待状态的节点从等待队列中移除。通过遍历等待队列,找到waitStatus不为-2的节点,并将其从队列中移除。

fullyRelease方法用于彻底释放锁资源。如果释放锁成功,则返回之前的锁状态;否则抛出异常。同时将节点状态置为取消。

CarOperation类详解

CarOperation类负责管理线程间的等待和唤醒操作。它首先创建了一个重入锁(Lock)和一个等待队列(Condition)。

等待操作(await)

在`await`方法中,首先获取锁资源,然后输出开始阻塞的信息。接着调用等待队列的`await`方法使当前线程进入等待状态。如果在等待过程中线程被中断,会捕获到InterruptedException并输出相关信息。无论是否发生异常,最后都需要释放锁资源。

唤醒操作(signal)

在`signal`方法中,首先获取锁资源,然后调用等待队列的`signal`方法唤醒等待队列中的第一个节点。唤醒操作完成后输出相关提示信息,并释放锁资源。

中断测试

在主方法中,创建一个CarOperation对象和一个线程。线程启动后执行`await`方法进入等待状态。主线程在一段时间后模拟发出中断信号,中断正在等待的线程。

先唤醒后中断测试

在这个测试中,主线程先启动一个线程并执行`await`方法。一段时间后,主线程先唤醒该线程,然后再中断它。

报告中断后的处理(reportInterruptAfterWait)

该方法根据中断模式决定是抛出异常还是重新中断。如果中断模式是抛出异常(THROW_IE),则抛出InterruptedException;如果是重新中断(REINTERRUPT),则调用`selfInterrupt`方法重新中断当前线程。

总结

当线程调用`await`方法时,它会将当前线程封装成节点加入等待队列尾部,并彻底释放锁资源。

如果当前节点不在同步队列中,线程会被挂起。

线程会自旋直到被中断或者被唤醒移动到同步队列中。

阻塞的节点会等待直到它获取到锁资源。

唤醒过程总结

唤醒的过程首先从等待队列的队首开始,尝试对队首节点执行唤醒操作。如果节点已经被取消了,就尝试唤醒下一个节点。对首节点执行唤醒时,会将节点转移到同步队列,然后根据前驱节点的状态决定是否直接唤醒当前节点对应的线程。

这个类的设计主要基于Java的并发编程基础,用于管理线程的等待和唤醒,确保线程间的正确协同工作。希望这个解释能帮助你更好地理解CarOperation类的功能和原理。如果还有其他疑问,欢迎交流讨论。

  丹凤千字科普:ie不能创建对象怎么办(详细资料介绍)