多线程笔记(十七):JUC 之 Condition 使用 + 原理分析

前言

       在 多线程笔记(十):wait()/notify()/notifyAll() 的用法 中,我们有介绍了 wait()、notify()方法的使用。

       在使用 Lock 锁之前,我们使用最多的同步方式就是 synchronized 关键字来实现同步方式了。然后配合 Object 的 wait()、notify()、notifyAll()方法,来实现线程的等待、唤醒操作

       现在我们已经了解了基于JUC工具包的Lock锁、AQS、CAS 等内容后,我们再来介绍一下这个 Condition 接口。

Condition介绍

       Condition 是伴随着JUC工具包,在Java 1.5 中才出现的。它用来替代传统的 Object 的 wait()、notify() 等来实现线程之间的协作。相比使用 Object 的 wait()、notify()、notifyAll(),使用 Condition的 await()、signal()、signalAll() 这种方式来实现线程之间的协作会更加的安全和高效。因为 Condition 也是基于AQS来实现的,所以通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。

        Condition 是一个接口,常用的方法就是 await()、signal()和signalAll()方法了。如果你想使用 Condition来实现线程之间的协作,因为 Condition 依赖于 Lock 接口,我们需要使用 lock.newCondition() 的方式来生成一个 Condition 类,然后使用新建的condition类来调用 await()、signal()等方法。但是这些都必须在先获得锁的情况下操作,即这些操作必须在 lock.lock() 和 lock.unlock() 之间才可以使用。

Condition 和 Object 对应关系
对应关系 Condition Object
等待 await() await()
唤醒 signal() notify()
唤醒全部等待线程 signalAll() notifyAll()

使用操作过程

  1. Condition 提供了 await() 方法将当前线程阻塞,提供了 signal()方法用于其他线程将已经阻塞的一个线程唤醒,signalAll()方法唤醒所有阻塞的线程;
  2. Condition需要结合 Lock 来一起使用;
  3. 线程调用 await()方法前必须获得锁,调用 await()方法时,将线程构造成 Node 节点加入到 AQS 的等待队列,同时释放锁,并使用 LockSupport.park() 方式来挂起当前线程;
  4. 其他线程调用 signal() 方法前,也必须先获得锁。当执行 signal() 方法时将 Condition 等待队列的 Node 节点移入到 AQS 的同步队列,当获得锁的线程释放锁时,将唤醒 AQS 同步队列的下个节点,并将下个节点设置为 head 节点,移除上一个已经释放锁的线程

Condtion图示

       Condition 提供的 await() 和 signal() 等方法,主要涉及到 AQS 同步队列 Condition 队列 之间等待线程关系交互。如下是两个队列具体干嘛的介绍。

Condition队列:用于存储处于等待状态的队列;          AQS队列:用于存储即将要获得锁的线程。

        如下图示过程,即为源码分析后得到的结果。为便于理解,此处不再进行源码分析。请看下图,你会很清楚的了解的。

Condition Demo

/**
 * await()线程
 */
public class ThreadWait extends Thread {

    private Lock lock;
    private Condition condition;

    public ThreadWait(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        try {
            lock.lock();
            System.out.println("开始执行 thread wait");
            try {
                condition.await();//此处有两个作用:1.使得当前的线程去阻塞   2.会释放锁(源码使用的LockSupport.unpark()方法)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("执行结束 thread wait");
        } finally {
            lock.unlock();
        }
    }
}
/**
 * signal()线程
 */
public class ThreadSignal extends Thread{

    private Lock lock;
    private Condition condition;

    public ThreadSignal(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        try {
            lock.lock();
            System.out.println("开始执行 thread signal");
            condition.signal();//阻塞
            System.out.println("执行结束 thread signal");
        } finally {
            lock.unlock();
        }
    }
}
/**
 * 测试await(),signal()
 */
public class test {

    public static void main(String[] args) throws InterruptedException {
        //定义Lock
        Lock lock = new ReentrantLock();
        //通过 lock.newCondition() 获得Condition对象(Lock和Condition必须同时使用)
        Condition condition = lock.newCondition();

        //Wait线程
        ThreadWait waitThread = new ThreadWait(lock,condition);
        waitThread.start();
        //Signal线程
        ThreadSignal notifyThread = new ThreadSignal(lock,condition);
        notifyThread.start();
        //Signal线程等待Wait线程启动(避免Signal线程先启动导致阻塞)
        notifyThread.join();
    }
}

测试结果

开始执行 thread wait
开始执行 thread signal
执行结束 thread signal
执行结束 thread wait

建议

      请对比:多线程笔记(十):wait()/notify()/notifyAll() 的用法 学习


Condition 使用 + 原理分析,介绍到此为止

如果本文对你有所帮助,那就给我点个赞呗 ^_^ 

End

发布了247 篇原创文章 · 获赞 44 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/lzb348110175/article/details/103781494