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日

相关文章

  • 一篇文章带你入门Java UML的类图

    下面我将为您详细讲解“一篇文章带你入门Java UML的类图”的完整攻略。 简介 Java UML是Java开发中必不可少的一部分,它可以帮助开发者对Java程序进行设计、开发和维护。其中类图是Java UML的重要组成部分。类图是一种描述类、接口、实现和它们之间关系的图形化方式。 步骤 下面我将向您介绍如何使用Java UML的类图进行Java编程: 1.…

    Java 2023年5月24日
    00
  • Spring中@Service注解的作用与@Controller和@RestController之间区别

    下面详细讲解“Spring中@Service注解的作用与@Controller和@RestController之间区别”。 @Service注解的作用 在Spring框架中,@Service注解是用于标记一个服务类的。与@Component注解类似,@Service注解的作用是告诉Spring框架,这个类是一个服务组件,需要被Spring框架管理。 与@Co…

    Java 2023年6月16日
    00
  • Java日期时间与正则表达式超详细整理(适合新手入门)

    Java日期时间与正则表达式都是重要的Java核心知识点,能够帮助开发者实现各种时间日期格式的处理以及字符串匹配等功能。下面就对Java日期时间与正则表达式进行详细讲解。 一、Java日期时间 1.1 日期时间的创建 Java提供了多种创建日期时间的方法,常见的有以下几种: 1.1.1 使用new Date()创建 使用java.util.Date类的默认构…

    Java 2023年5月20日
    00
  • 解析C#彩色图像灰度化算法的实现代码详解

    接下来我将根据题目要求,详细讲解“解析C#彩色图像灰度化算法的实现代码详解”的完整攻略。 一、什么是灰度化算法 灰度化算法是图像处理中的一种重要操作,将彩色图像转化为灰度图像。在灰度图像中,每个像素点只保存一个灰度值,代表了该像素点在黑白色阶上的明暗程度。灰度图像通常比彩色图像更加简洁、易于处理。 二、C#彩色图像灰度化算法的实现 1. 方法一:加权平均法 …

    Java 2023年5月19日
    00
  • java中的switch case语句使用详解

    关于“java中的switch case语句使用详解”的攻略,我来给你详细讲解一下。 一、介绍 在 Java 中,switch…case 是一种多重分支语句,用于测试一个变量等于多个值中的哪一个。虽然它们在某些情况下可以与 if 语句互换使用,但它们具有更高的可读性和性能。在下面的示例中,将详细介绍如何使用 switch 语句。 二、语法 下面是一个sw…

    Java 2023年5月20日
    00
  • Java实现的最大匹配分词算法详解

    Java 实现最大匹配分词算法详解 什么是最大匹配分词算法? 最大匹配分词算法是目前中文分词中最简单、最易于实现的一种方法。该算法采用正向最大匹配或逆向最大匹配的方式,将整段文本按照给定的词典进行分词,从而得到一个完整的分词结果列表。 最大匹配分词算法的实现步骤 读取待分词的文本和词典,将词典中的所有词按照长度从大到小进行排序,这是为了保证匹配时能够优先匹配…

    Java 2023年5月19日
    00
  • 详解Ubuntu下安装和配置Apache2

    下面是详解Ubuntu下安装和配置Apache2的完整攻略步骤: 1. 安装Apache2 Ubuntu上安装Apache2十分简单,只需要在终端中运行以下命令即可: sudo apt-get update sudo apt-get install apache2 2. 启动Apache2服务 安装完成后,需要将Apache2服务启动才能访问。运行以下命令启…

    Java 2023年5月19日
    00
  • Spring Security实现自定义访问策略

    下面是关于Spring Security实现自定义访问策略的完整攻略,主要分为以下几个步骤: 定义自定义访问策略类 首先需要定义一个实现了AccessDecisionVoter接口的自定义访问策略类。该类主要实现AccessDecisionVoter接口中的vote()方法,根据自己的逻辑决定是否允许当前用户访问对应的资源。下面是一个简单的示例代码: pub…

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