首页 > 图灵资讯 > 技术篇>正文

java中Condition接口原理及实现

2023-04-06 14:46:07

Condition在java 它出现在1.5中,用来取代传统的Objectwait()、notify()与Object的wait()相比,实现线程间的合作、notify(),使用Condition的await()、signal()这样,线程间合作才能更安全、更高效。因此一般情况下比较推荐使用Condition,阻塞队列实际上使用Condition来模拟线程间合作。java中Condition接口原理及实现这是一个复杂的过程。

Java里 sychronized和Lock+Condtion 都属于管程模型,Condition 管程模型代表等待条件。Condition在Lock的基础上使用,在原Lock的基础上,我们可以更有针对性,更灵活地协调多种条件下的线程协调。

每个 Condition 对象包含一个等待队列。在同步器中,同步队列中的节点被重复使用。Condition 对象的 await 和 signal 操作是等待队列和同步队列的操作。

await 操作:将当前线程构建成节点并添加到等待队列中,然后释放同步状态,唤醒同步队列中的后继节点,最后进入等待状态。

signal 操作:唤醒在等待队列中等待时间最长的节点(第一节点),并在唤醒节点之前将节点移动到同步队列。唤醒后的节点尝试竞争锁(旋转)。

首先,让我们通过一个简单的例子来看Condition的使用方法

public static void main(String[] args) {

final ReentrantLock reentrantLock = new ReentrantLock();

final Condition condition = reentrantLock.newCondition();

Thread thread = new Thread((Runnable) () -> {

try {

reentrantLock.lock();

System.out.println("我要等一个新信号" + this);

condition.await();///这里需要注意的是,这是await,而不是wait

}

catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("得到一个信号!!" + this);

reentrantLock.unlock();

}, "waitthread1");

thread.start();

Thread thread1 = new Thread((Runnable) () -> {

reentrantLock.lock();

System.out.println("我拿到锁了");

try {

Thread.sleep(3000);

}

catch (InterruptedException e) {

e.printStackTrace();

}

condition.signalAll();

System.out.println("我发出一个信号!!");

reentrantLock.unlock();

}, "signalThread");

thread1.start();

}

运行结果如下:

我要等一个新信号lock.ReentrantLockTest$1@a62fc3

我拿到锁了

我发出一个信号!!

得到一个信号!!

从上面的例子可以看Condition的执行方法是在线程1中调用await方法后,线程1释放锁,睡觉等待唤醒。线程2获得锁后,开始做事。完成后,调用Condition的signal方法唤醒线程1,线程1恢复执行。说明Condition是一种多线程间协调通信的工具,使某个或某个线程能够一起等待某个条件(Condition),只有具备了这个条件( signal 或者 当带调用signalalll方法时, ,这些等待线程会被唤醒,从而重新争夺锁。

接下来分析一下Condition接口的原理:

Conditionobject是同步器AbstractQuedsynchronizer的内部类。由于Condition的操作需要获得相关的锁,因此作为同步器的内部类也更为合理。每个Condition对象都包含一个队列,这是Condition对象实现等待/通知功能的关键。以下是对Condition实现的分析,主要包括:等待队列、等待和通知

等待队列是一个FIFO队列在队列中的每个节点都包含一个线程引用,即在Condition对象上等待的线程。如果一个线程调用Condition.await()方法,线程将释放锁,构建节点加入等待队列并进入等待状态一个Condition包含一个等待队列,Condition有第一个节点(firstWaiter)和尾节点(lastWaiter)。Condition当前线程调用.await()方法将用当前线程构建节点,并从尾部添加节点等待队列

调用Conditionawait()方法(或从await开始)将使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。从await()方法返回时,当前线程必须获得与Condition相关的锁。如果从队列(同步队列和等待队列)的角度看await()方法,当调用await()方法时,相当于同步队列的第一个节点(获得锁的节点)移动到等待队列的Condition

调用Conditionsignal()方法将唤醒在等待队列中等待时间最长的节点(第一节点),并在唤醒节点之前将节点移动到同步队列。

public final void signal() {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

Node first = firstWaiter;

if (first != null)

doSignal(first);

}

调用该方法的前提条件是当前线程必须获得锁,可见signal()方法检查了isheldexclusively(),即当前线程必须是锁定线程。然后获得等待队列的第一个节点,将其移动到同步队列,并使用locksupport唤醒节点中的线程节点从等待队列移动到同步队列。

通过调用同步器enq(Node node)方法,等待队列中的头节点线程安全移动到同步队列。当节点移动到同步队列时,当前线程用LockSuport唤醒节点的线程。被唤醒的线程将从await()方法的while循环中退出(isOnSyncQueue(Node node)方法返回true,节点已经在同步队列中),然后将同步器的acquireQued()方法调用到获得同步状态的竞争中。在成功获得同步状态(或锁定)后,唤醒的线程将从之前调用的await()方法返回,此时该线程已成功获得锁定。ConditionsignalAll()方法相当于对等待队列中的每个节点进行一次signal()方法,其效果是将等待队列中的所有节点移动到同步队列中,唤醒每个节点的线程。

我说了很多,但我还是没有说得很详细。读完这么多内容后我无法理解。消化消化需要一定的时间。其实Condition接口只是java众多接口中的沧海一粟,所以学好java还有很长的路要走。当然,如果你想了解更多关于java接口的知识,可以免费查看蛙课网。Java面向接口课程

上一篇 Java面试必备15道设计模式面试题
下一篇 你不知道的多线程ThreadLocal

文章素材均来源于网络,如有侵权,请联系管理员删除。