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的完整攻略。

阅读剩余 78%

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

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

相关文章

  • Mybatis之映射实体类中不区分大小写的解决

    一、问题背景 在Mybatis中,如果数据库表中的列名按照大写或小写不一定区分,可能会导致实体类中的属性无法映射到对应的列上,从而导致查询或插入数据的时候出现错误。因此,我们需要解决这个问题。 二、解决方案 Mybatis提供了一个配置选项来指定列名和属性名的命名规则,可以解决列名大小写不一致的问题。配置方式如下: 1.配置 mybatis-config.x…

    Java 2023年5月20日
    00
  • 浅谈hibernate之映射文件VS映射注解

    如何选择使用Hibernate的映射文件或映射注解?这是Hibernate初学者常常疑惑的问题。本文将深入浅出地介绍这个话题,帮助读者更好地掌握Hibernate的使用方法。 什么是映射文件? Hibernate的映射文件定义了Java类和数据库表之间的映射关系。映射文件只是一个XML格式的文件,用于Hibernate根据属性及其映射关系创建数据表和对象。H…

    Java 2023年5月19日
    00
  • java实现微信H5支付方法详解

    Java实现微信H5支付方法详解 微信H5支付是一种便捷的支付方式,它允许用户在微信H5浏览器中完成支付。在Java中,我们可以使用微信支付官方提供的Java SDK来实现微信H5支付功能。 以下是实现微信H5支付的详细步骤: Step1:获取微信支付相关信息 首先,我们需要去申请微信支付的相关信息,包括商户号和应用密钥等。申请完成后,我们可以在微信商户平台…

    Java 2023年6月15日
    00
  • SpringBoot中定时任务@Scheduled的多线程使用详解

    下面是关于“SpringBoot中定时任务@Scheduled的多线程使用详解”的攻略,分为以下三个部分: 1. 什么是@Scheduled注解 SpringBoot中的@Scheduled注解是用来标记方法执行定时任务的注解。使用该注解能够非常方便地实现某些任务的周期性执行。@Scheduled注解可以设置的属性如下: fixedRate:指定任务开始后每…

    Java 2023年5月19日
    00
  • Java的Struts框架中配置国际化的资源存储的要点解析

    Java的Struts框架支持使用国际化(i18n)来为不同语言的用户提供不同的用户界面。在Struts中配置国际化的资源存储主要包括三个要点,分别是资源文件的命名规则、资源文件的组织结构以及使用资源文件的方法。 资源文件的命名规则 Struts框架支持使用.properties文件来存储国际化资源信息,文件的名称要遵循一定的命名规则。文件名由以下三部分组成…

    Java 2023年5月20日
    00
  • Java深入分析与解决Top-K问题

    Java深入分析与解决Top-K问题 什么是Top-K问题? Top-K问题是指在一个元素集合中,找出排名前K的元素,其中K通常是一个比较小的数字。例如,在一个学生考试成绩的集合中,要找出排名前5的学生。 解决Top-K问题有很多方法,不同的方法的时间复杂度和空间复杂度各不相同。本文将介绍两种常用的方法:堆排序和快速排序。 堆排序 概述 堆排序利用了堆这种数…

    Java 2023年5月19日
    00
  • 一篇文章带你了解Java基础-多态

    一篇文章带你了解Java基础-多态 前言 多态是Java中一个比较重要的概念,也是Java语言中的一种基本特征。掌握好多态,可以写出更加优雅、灵活、可扩展的代码。本文将从多态的概念入手,介绍Java中的多态,帮助大家更好地学习和使用Java语言。 什么是多态 多态是指同一对象在不同情况下有不同的表现形式,即同一种行为具有不同的表现形式和状态。在Java语言中…

    Java 2023年5月26日
    00
  • java9新特性Reactive Stream响应式编程 API

    Java 9 增加了 Reactive Stream 响应式编程 API,使得开发者能够更方便地实现响应式编程。本文将详细解释 Reactive Stream API 的用法,并提供示例代码来说明。 Reactive Stream 简介 Reactive Stream 是一种用于异步编程的编程模型,它能够处理大数据流和异步操作。Reactive Stream…

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