AQS 中的 Condition 对象是用于实现线程间通信和同步的对象,它可以和锁对象一起使用。Condition 对象的实现依赖于等待队列,它的主要作用是将等待某个条件的线程从等待队列中移动到条件队列中,同时在条件满足时将它们从条件队列中移回到等待队列中。

1、方法介绍

1)Condition 对象的创建:Condition 对象是通过 Lock 接口中的 newCondition() 方法创建的。具体实现可以查看 ReentrantLock 和 ReentrantReadWriteLock 类中的 newCondition() 方法。

2)await() 方法的实现:await() 方法用于将当前线程加入到条件队列中,并且释放锁。具体实现中,它首先需要获取到当前线程对应的节点(Node 对象),然后将节点从等待队列中移动到条件队列中,最后调用 release() 方法释放锁,并且挂起当前线程。

3)signal() 方法的实现:signal() 方法用于唤醒一个等待在条件队列中的线程,并且将它从条件队列中移动到等待队列中。具体实现中,它首先需要获取到条件队列的头节点(firstWaiter),然后遍历条件队列,找到第一个不为 null 的节点,并且将它从条件队列中移动到等待队列中,最后调用 unpark() 方法唤醒线程。

4)signalAll() 方法的实现:signalAll() 方法用于唤醒所有等待在条件队列中的线程,并且将它们从条件队列中移动到等待队列中。具体实现中,它和 signal() 方法类似,只是在遍历条件队列时,将所有节点都移动到等待队列中。

需要注意的是,Condition 对象的实现依赖于等待队列和 AQS 的实现,它的底层原理比较复杂。在使用 Condition 对象的时候,需要特别注意锁的释放和获取顺序,以避免死锁等问题的出现。

2、AQS的哪些锁的实现用到了condition对象

AQS 中的 ReentrantLock 和 ReentrantReadWriteLock 都使用了 Condition 对象实现等待/通知机制。
1)在 ReentrantLock 中,Condition 对象是通过 newCondition() 方法创建的,它是基于 AQS 的等待/通知机制实现的。ReentrantLock 中的 Condition 对象和 ReentrantLock 对象是绑定的,即同一个 ReentrantLock 对象中可以创建多个 Condition 对象。线程在使用 Condition 对象等待某个条件时,需要先获取对应的锁,然后通过 Condition 对象的 await() 方法进入等待状态,并且释放锁。当条件满足时,线程可以通过 signal() 或 signalAll() 方法唤醒等待在条件队列中的线程,让它们重新进入到等待队列中等待获取锁。
2)在 ReentrantReadWriteLock 中,ReadLock 和 WriteLock 都实现了 Condition 接口。它们分别使用了两个 Condition 对象,分别用于实现读线程等待写线程释放锁的机制和写线程等待读线程释放锁的机制。

3、ReentrantLock 中 Condition 使用demo

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;

    public void produce() {
        lock.lock();
        try {
            // 检查共享数据状态,如果共享数据已经准备好,就等待
            while (ready) {
                condition.await();
            }
            // 生产共享数据
            System.out.println("Producing data...");
            // 标记共享数据状态
            ready = true;
            // 唤醒消费者线程
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void consume() {
        lock.lock();
        try {
            // 检查共享数据状态,如果共享数据还没有准备好,就等待
            while (!ready) {
                condition.await();
            }
            // 消费共享数据
            System.out.println("Consuming data...");
            // 标记共享数据状态
            ready = false;
            // 唤醒生产者线程
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        final ConditionDemo demo = new ConditionDemo();

        // 创建生产者线程
        Thread producer = new Thread(() -> {
            while (true) {
                demo.produce();
            }
        });

        // 创建消费者线程
        Thread consumer = new Thread(() -> {
            while (true) {
                demo.consume();
            }
        });

        // 启动生产者和消费者线程
        producer.start();
        consumer.start();
    }
}

在这个示例代码中,我们创建了一个 ConditionDemo 类,其中包含了一个 ReentrantLock 和一个 Condition 对象。我们通过 acquire() 和 release() 方法获取和释放锁,并且通过 await() 和 signalAll() 方法实现了线程间通信和同步。

具体实现中,我们在 produce() 方法中获取锁并且检查共享数据状态,如果共享数据已经准备好,就等待,否则就生产共享数据,并且唤醒消费者线程。在 consume() 方法中,我们也获取锁并且检查共享数据状态,如果共享数据还没有准备好,就等待,否则就消费共享数据,并且唤醒生产者线程。

最后,我们通过创建生产者和消费者线程,并且启动它们来模拟线程间通信和同步的过程。当运行这个示例代码时,我们会看到生产者和消费者线程交替运行,并且输出相应的生产和消费信息。这个示例代码展示了如何使用 ReentrantLock 和 Condition 实现线程间通信和同步,它可以用作学习和参考多线程编程的例子。


AQS-基本篇(1)
AQS-基本原理17问(2)
AQS-Condition对象的使用(3)
AQS-场景共享锁和独占锁(4)


无知
0 声望1 粉丝

思考中


引用和评论

0 条评论