丹凤千字科普: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类的功能和原理。如果还有其他疑问,欢迎交流讨论。
