Java多线程之条件对象Condition

Java多线程中的条件对象Condition是在java.util.concurrent.locks包下的,它和synchronized关键字一样,可以协调线程的执行顺序和通信,不过其功能更为强大,可用于等待条件、通知单个线程和通知所有等待线程。

一、条件对象Condition的基本用法

1. 创建Condition对象

在使用Condition对象前,需要首先创建其关联的Lock对象。

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

2. await方法

由于Condition对象是和Lock对象关联的,所以在执行condition.await()方法之前,必须先获取锁。然后,await()方法将会释放该锁并进入等待状态,等待其他线程调用相应的条件通知方法将其唤醒。

lock.lock(); // 获取锁

try {
    while(条件不满足) {
        condition.await(); // 释放锁并等待条件满足
    }
    // 条件满足,继续执行其他操作
} 
catch (InterruptedException e) {
    e.printStackTrace();
} 
finally {
    lock.unlock(); // 释放锁
}

3. signal方法和signalAll方法

当某些条件满足时,线程应该通知其他正在等待该条件的线程,这时候,可以使用signal()或signalAll()方法。

  • signal()方法:随机选择一条等待该条件的线程进行通知,一般情况下不推荐使用。
  • signalAll()方法:通知所有等待该条件的线程。
lock.lock(); // 获取锁

try {
    // 等待条件满足
    while(条件不满足) {
        condition.await();
    }
    // 条件满足,通知其他线程
    condition.signalAll();
}
catch (InterruptedException e) {
    e.printStackTrace();
}
finally {
    lock.unlock(); // 释放锁
}

二、示例1:生产者和消费者问题

考虑一个经典的生产者和消费者问题,在此过程中,生产者和消费者同时运行,但在不同的时间执行。生产者生成产品并将其放入队列中,而消费者则从队列中取出产品并进行消费。

Java中的阻塞队列是典型的实现方式,而Condition则可以通过控制队列中元素的数量实现阻塞和解锁。

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

public class ProducerConsumerDemo {
    final LinkedList<Integer> queue = new LinkedList<>();
    final int capacity = 10;

    final Lock lock = new ReentrantLock();
    final Condition notEmpty = lock.newCondition();
    final Condition notFull = lock.newCondition();

    public void produce(int num){
        lock.lock();
        try{
            while(queue.size() == capacity){
                // 队列已满,等待队列释放空位
                notFull.await();
            }
            for(int i = 0; i < num; ++i){
                queue.add(i);
            }
            System.out.println("生产 " + num + " 个产品,当前队列长度为 " + queue.size());
            // 队列非空,唤醒消费者
            notEmpty.signalAll();                 
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void consume(int num){
        lock.lock();
        try{
            while(queue.size() == 0){
                  // 队列为空,等待队列有新的产品
                notEmpty.await();
            }
            for(int i = 0; i < num; ++i){
                queue.remove();
            }
            System.out.println("消费 " + num + " 个产品,当前队列长度为 " + queue.size());
              // 队列未满,唤醒生产者
            notFull.signalAll();                
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    } 
}

在生产者和消费者的类中,创建了空的LinkedList队列,队列大小限制为10。当生产者启动时,它将连续生产num个项目,并将它们添加到队列中。队列使用锁对同步化访问进行保护,并使用条件对象notEmpty和notFull调用await和signalAll方法。

三、示例2:条件队列的测试和使用

假设我们希望封装多个线程对象作为一个辅助类对象。这个线程安全的队列最初创建为一(非常)小的列表,其阈值设为零,当它既满又空时,能够适当地阻塞既生产者又消费者。

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

public class ConditionQueueDemo {
    private LinkedList<Integer> storage;
    private int maxSize = 10;
    private Lock lock;
    private Condition isFull;
    private Condition isEmpty;

    public ConditionQueueDemo(int size) {
        maxSize = size;
        storage = new LinkedList<>();
        lock = new ReentrantLock();
        isFull = lock.newCondition();
        isEmpty = lock.newCondition();
    }

    public void put(int num) throws InterruptedException {
        lock.lock();
        try {
            while (storage.size() == maxSize) { // 若队列已满
                System.out.println(Thread.currentThread().getName()+ " 队列已满,等待...");
                isFull.await(); // 阻塞生产者线程,等待非满信号
            }
            Random random = new Random();
            Thread.sleep(random.nextInt(1000)); // 随机等待
            storage.add(num); // 将数值num加入队列尾部
            System.out.println("[" + Thread.currentThread().getName() + "] 生产者 => " 
                             + num + ", 当前队列长度为:[" + storage.size() + "]/" + maxSize);
            isEmpty.signalAll(); // 通知所有的消费者线程
        } finally {
            lock.unlock();
        }
    }

    public void get() throws InterruptedException {
        lock.lock();
        try {
            while (storage.size() == 0) { // 若队列已空
                System.out.println(Thread.currentThread().getName() + " 队列已空,等待...");
                isEmpty.await(); // 阻塞消费者线程,等待非空信号
            }
            Random random = new Random();
            Thread.sleep(random.nextInt(1000)); // 随机等待
            int val = storage.poll(); // 从队列头部弹出一个数值
            System.out.println("[" + Thread.currentThread().getName() + "] 消费者 => " 
                             + val + ", 当前队列长度为:[" + storage.size() + "]/" + maxSize);
            isFull.signalAll(); // 通知所有的生产者线程
        } finally {
            lock.unlock();
        }
    }

    public int size() {
        return storage.size();
    }

    public static void main(String[] args) {
        ConditionQueueDemo queue = new ConditionQueueDemo(5);

        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                try {
                    while (true) {
                        queue.put(new Random().nextInt(100)); // 随机数加入队列
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "Producer").start();
        }

        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                try {
                    while (true) {
                        queue.get(); // 从队列中获取数值
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "Consumer").start();
        }
    }
}

在示例中,我们通过随机生成的数值执行生产和消费任务,在队列已满或队列已空的情况下,线程通过调用await()方法阻塞并等待队列空或队列非满状态。在这两种情况下,线程被阻塞,等待相应程度的唤醒。如果在相应时间内访问了空队列或非空队列,则队列中的线程将被唤醒并继续运行。

以上就是Java多线程之条件对象Condition的完整攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程之条件对象Condition - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Maven打包并生成运行脚本的示例代码

    这里是Maven打包并生成运行脚本的完整攻略,包含两个示例代码。 1. Maven打包过程 在使用Maven进行打包之前,需要在项目的pom.xml文件中添加以下插件: <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupI…

    Java 2023年5月20日
    00
  • java实现联机五子棋

    Java实现联机五子棋完整攻略 引言 联机五子棋是一种经典的、非常受欢迎的棋类游戏。在实现联机五子棋游戏过程中,需要采用 Java编程语言 进行设计和开发。本文将为您提供一份完整的攻略,指导您如何使用 Java实现联机五子棋游戏。 前置技能 在开始实现联机五子棋游戏前,我们需要掌握以下技能: 掌握 Java编程语言 ; 对 Java I/O相关API的使用必…

    Java 2023年5月19日
    00
  • Java验证时间格式是否正确方法类项目实战

    Java验证时间格式是否正确方法类项目实战 介绍 在Java开发过程中,经常需要验证时间日期格式是否正确,例如用户提交的时间日期格式是否符合规范,或者我们需要对某个日期字符串进行解析等等。这篇文章将介绍如何在Java中验证时间日期格式是否正确的方法类项目实战。 步骤 步骤一:创建时间格式验证工具类 我们可以创建一个名为 DateTimeUtil 的工具类来进…

    Java 2023年5月20日
    00
  • struts2 jquery 打造无限层次的树

    确保能够正确的讲解 “struts2 jquery 打造无限层次的树” 这一话题,我们需要先分析以下这个主题的三个关键词: struts2、jquery、树。本文将结合这三个关键词,详细讲解 “struts2 jquery 打造无限层次的树” 的完整攻略。具体的攻略内容如下: 1. 引入Struts2 首先,我们需要在项目中引入 Struts2,具体方式如下…

    Java 2023年6月16日
    00
  • Java项目开发中实现分页的三种方式总结

    Java项目开发中实现分页的三种方式总结 在Java项目的开发过程中,经常需要对列表数据进行分页显示。本篇文章将总结Java项目开发中实现分页的三种方式,以供参考。 第一种方式:使用分页插件 分页插件是一种在MyBatis框架中常用的解决方案,它可以方便地实现分页功能。下面是使用MyBatis的一个示例: <!– 配置分页插件 –> <…

    Java 2023年6月16日
    00
  • java 一键部署 jar 包和 war 包

    一键部署是指通过单击一个按钮或执行一个脚本就可以完成整个软件部署的过程,这在提高开发效率以及方便用户安装等方面具有重要的意义。下面我来详细讲解“Java 一键部署 jar 包和 war 包”的完整攻略。 一、jar 包的一键部署 Java 编写的应用程序一般打成 jar 包进行部署。在进行 jar 包一键部署时,可以通过以下步骤实现: 1. 建立 Maven…

    Java 2023年5月24日
    00
  • commons fileupload实现文件上传的实例代码

    当我们需要在Web应用程序中实现文件上传功能时,可以使用Apache Commons FileUpload库来实现这个功能。下面将详细讲解如何使用该库来实现文件上传功能,包括添加依赖、编写实现代码等过程。 添加依赖 首先,在Maven项目中添加以下依赖项: <dependency> <groupId>commons-fileuploa…

    Java 2023年6月15日
    00
  • java字符串中${}或者{}等的占位符替换工具类

    Java字符串中 ${} 或 {} 等占位符用于在字符串中嵌入变量,以便动态地构造字符串。在实际应用中,我们可能需要对包含占位符的字符串进行替换,这时候我们可以使用工具类来实现占位符替换功能。下面是占位符替换的完整攻略: 步骤一:创建工具类 创建一个 Java 工具类,用于实现占位符替换功能。核心代码如下: public class PlaceholderU…

    Java 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部