锁机制之 Condition 接口
1. 前言
本节内容主要是对 Java 锁机制之 Condition 接口进行讲解,Condition 接口是配合 Lock 接口使用的,我们已经学习过 Lock 接口的相关知识,那么接下来对 Condition 接口进行讲解。本节内容的知识点如下:
- Condition 接口简介,这是我们认识 Condition 接口的基础;
- Condition 接口定义,整体上先了解 Condition 接口所包含的方法,为基础内容;
- Condition 接口所提供的方法与 Object 所提供的方法的区别与联系,此部分为本节的重点之一;
- Condition 对象的创建方式,这是开始使用 Condition 的前提,需要牢记;
- Condition 接口的常用方法使用,这是本节课程的核心内容,掌握 Condition 接口使用方式,也是我们本节课程的最终目的所在;
- 使用 ReentrantLock 与 Condition 接口,实现生产者与消费者模式。
2. Condition 接口简介
任意一个 Java 对象,都拥有一组监视器方法(定义在 java.lang.Object 上),主要包括 wait ()、wait (long timeout)、notify () 以及 notifyAll () 方法。这些方法与 synchronized 同步关键字配合,可以实现等待 / 通知模式。
定义:Condition 接口也提供了类似 Object 的监视器方法,与 Lock 配合可以实现等待 / 通知模式。Condition 可以看做是 Obejct 类的 wait ()、notify ()、notifyAll () 方法的替代品,与 Lock 配合使用。
3. Condition 接口定义
我们看到,从 JDK 的源码中可以获悉,Condition 接口包含了如下的方法,对于其中常用的方法,我们在后续的内容中会有比较详细的讲解。
public interface Condition {
void await() throws InterruptedException;
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}
4. Condition 方法与 Object 方法的联系与区别
联系 1:都有一组类似的方法.
- Object 对象监视器: Object.wait()、Object.wait(long timeout)、Object.notify()、Object.notifyAll()。
- Condition 对象: Condition.await()、Condition.awaitNanos(long nanosTimeout)、Condition.signal()、Condition.signalAll()。
联系 2:都需要和锁进行关联。
- Object 对象监视器: 需要进入 synchronized 语句块(进入对象监视器)才能调用对象监视器的方法。
- Condition 对象: 需要和一个 Lock 绑定。
区别:
- Condition 拓展的语义方法,如 awaitUninterruptibly () 等待时忽略中断方法;
- 在使用方法时,Object 对象监视器是进入 synchronized 语句块(进入对象监视器)后调用 Object.wait ()。而 Condition 对象需要和一个 Lock 绑定,并显示的调用 lock () 获取锁,然后调用 Condition.await ();
- 从等待队列数量看,Object 对象监视器是 1 个。而 Condition 对象是多个。可以通过多次调用 lock.newCondition () 返回多个等待队列。
5. Condition 对象的创建
Condition 对象是由 Lock 对象创建出来的 (Lock.newCondition),换句话说,Condition 是依赖 Lock 对象的。那么我们来看看如果创建 Condition 对象。
此处仅提供示例代码,后续我们在进行方法讲解时,会有全部的代码示例,但在学习使用方法之前,我们必须先学会如何创建。
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
6. Condition 方法介绍
等待机制方法简介:
- void await() throws InterruptedException:当前线程进入等待状态,直到被其它线程的唤醒继续执行或被中断;
- void awaitUninterruptibly():当前线程进入等待状态,直到被其它线程被唤醒;
- long awaitNanos(long nanosTimeout) throws InterruptedException:当前线程进入等待状态,直到被其他线程唤醒或被中断,或者指定的等待时间结束;nanosTimeout 为超时时间,返回值 = 超时时间 – 实际消耗时间;
- boolean await(long time, TimeUnit unit) throws InterruptedException:当前线程进入等待状态,直到被其他线程唤醒或被中断,或者指定的等待时间结束;与上个方法区别:可以自己设置时间单位,未超时被唤醒返回 true,超时则返回 false;
- boolean awaitUntil(Date deadline) throws InterruptedException:当前线程等待状态,直到被其他线程唤醒或被中断,或者指定的截止时间结束,截止时间结束前被唤醒,返回 true,否则返回 false。
通知机制方法简介:
- void signal():唤醒一个线程;
- void signalAll():唤醒所有线程。
7. ReentrantLock 与 Condition 实现生产者与消费者
非常熟悉的场景设计,这是我们在讲解生产者与消费者模型时使用的案例设计,那么此处有细微的修改如下,请学习者进行比照学习,印象更加深刻。
场景修改:
- 创建一个工厂类 ProductFactory,该类包含两个方法,produce 生产方法和 consume 消费方法(未改变);
- 对于 produce 方法,当没有库存或者库存达到 10 时,停止生产。为了更便于观察结果,每生产一个产品,sleep 3000 毫秒(5000 变 3000,调用地址也改变了,具体看代码);
- 对于 consume 方法,只要有库存就进行消费。为了更便于观察结果,每消费一个产品,sleep 5000 毫秒(sleep 调用地址改变了,具体看代码);
- 库存使用 LinkedList 进行实现,此时 LinkedList 即共享数据内存(未改变);
- 创建一个 Producer 生产者类,用于调用 ProductFactory 的 produce 方法。生产过程中,要对每个产品从 0 开始进行编号 (新增 sleep 3000ms);
- 创建一个 Consumer 消费者类,用于调用 ProductFactory 的 consume 方法 (新增 sleep 5000ms);
- 创建一个测试类,main 函数中创建 2 个生产者和 3 个消费者,运行程序进行结果观察(未改变)。
实例:
public class DemoTest {
public static void main(String[] args) {
ProductFactory productFactory = new ProductFactory();
new Thread(new Producer(productFactory),"1号生产者"). start();
new Thread(new Producer(productFactory),"2号生产者"). start();
new Thread(new Consumer(productFactory),"1号消费者"). start();
new Thread(new Consumer(productFactory),"2号消费者"). start();
new Thread(new Consumer(productFactory),"3号消费者"). start();
}
}
class ProductFactory {
private LinkedList<String> products; //根据需求定义库存,用 LinkedList 实现
private int capacity = 10; // 根据需求:定义最大库存 10
private Lock lock = new ReentrantLock(false);
private Condition p = lock.newCondition();
private Condition c = lock.newCondition();
public ProductFactory() {
products = new LinkedList<String>();
}
// 根据需求:produce 方法创建
public void produce(String product) {
try {
lock.lock();
while (capacity == products.size()) { //根据需求:如果达到 10 库存,停止生产
try {
System.out.println("警告:线程("+Thread.currentThread().getName() + ")准备生产产品,但产品池已满");
p.await(); // 库存达到 10 ,生产线程进入 wait 状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products.add(product); //如果没有到 10 库存,进行产品添加
System.out.println("线程("+Thread.currentThread().getName() + ")生产了一件产品:" + product+";当前剩余商品"+products.size()+"个");
c.signalAll(); //生产了产品,通知消费者线程从 wait 状态唤醒,进行消费
} finally {
lock.unlock();
}
}
// 根据需求:consume 方法创建
public String consume() {
try {
lock.lock();
while (products.size()==0) { //根据需求:没有库存消费者进入wait状态
try {
System.out.println("警告:线程("+Thread.currentThread().getName() + ")准备消费产品,但当前没有产品");
c.await(); //库存为 0 ,无法消费,进入 wait ,等待生产者线程唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String product = products.remove(0) ; //如果有库存则消费,并移除消费掉的产品
System.out.println("线程("+Thread.currentThread().getName() + ")消费了一件产品:" + product+";当前剩余商品"+products.size()+"个");
p.signalAll();// 通知生产者继续生产
return product;
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable {
private ProductFactory productFactory; //关联工厂类,调用 produce 方法
public Producer(ProductFactory productFactory) {
this.productFactory = productFactory;
}
public void run() {
int i = 0 ; // 根据需求,对产品进行编号
while (true) {
productFactory.produce(String.valueOf(i)); //根据需求 ,调用 productFactory 的 produce 方法
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
class Consumer implements Runnable {
private ProductFactory productFactory;
public Consumer(ProductFactory productFactory) {
this.productFactory = productFactory;
}
public void run() {
while (true) {
productFactory.consume();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果验证:
线程(1号生产者)生产了一件产品:0;当前剩余商品1个
线程(2号生产者)生产了一件产品:0;当前剩余商品2个
线程(1号消费者)消费了一件产品:0;当前剩余商品1个
线程(2号消费者)消费了一件产品:0;当前剩余商品0个
警告:线程(3号消费者)准备消费产品,但当前没有产品
线程(2号生产者)生产了一件产品:1;当前剩余商品1个
线程(1号生产者)生产了一件产品:1;当前剩余商品2个
线程(3号消费者)消费了一件产品:1;当前剩余商品1个
线程(2号消费者)消费了一件产品:1;当前剩余商品0个
警告:线程(1号消费者)准备消费产品,但当前没有产品
8. 小结
本节内容为主要对 Condition 接口进行了讲解,Condition 接口作为 Lock 接口的监视器,是非常重要的接口,我们需要非常重视 Condition 接口的学习。
本节内容最终的目的是使用 Condition 接口和 Lock 配合实现案例,核心内容即为 Condition 接口的使用,请翻看生产者与消费者一节,对比进行学习。
最新评论
1.4 可以嘛?
可以
2022.06.06版本2022.1 临时激活码激活提示key is invalid 热心大佬提供的激活码里跟文章里的激活码是一样的 坐等lz掘地三尺
我打开绿色版,用配色方案的时候,还是弹出付费
非常好用,谢谢!lz好人一生平安!
感谢楼主
东西不错,希望能够继续保持
这个win版破解之后,就不要激活吧~