Java wait和notifyAll实现简单的阻塞队列

让我来为你详细讲解如何使用Java的wait和notifyAll实现简单的阻塞队列。

什么是阻塞队列

阻塞队列是一种特殊的队列,与普通队列的区别在于,当队列满时,往队列中添加元素的操作会被阻塞,直到队列不满;而当队列为空时,从队列中取出元素的操作会被阻塞,直到队列不为空。

阻塞队列在多线程环境下使用更加安全,它可以帮助我们解决线程同步和协作的问题。

使用wait和notifyAll实现阻塞队列

使用Java的wait和notifyAll功能,我们可以比较方便地实现一个阻塞队列。具体实现过程如下:

  1. 定义一个阻塞队列类,该类中包含一个固定大小的数组用于存储元素。
  2. 定义一个计数器变量,用于记录队列中元素的个数。
  3. 定义两个锁对象(生产者锁和消费者锁),以及两个条件变量(队列不满和队列不空)。
  4. 实现阻塞队列的入队方法put和出队方法take。入队方法put将元素添加到队列中,若队列已满,则阻塞等待;出队方法take将队列中的元素取出,并将队列中的元素个数减一,若队列为空,则阻塞等待。

下面是基于上述实现思路的Java代码示例:

public class BlockingQueue<T> {
    private T[] queue;
    private int count;
    private int size;

    private Object putLock = new Object();
    private Object takeLock = new Object();

    private Condition notFull = putLock.newCondition();
    private Condition notEmpty = takeLock.newCondition();

    public BlockingQueue(int size) {
        queue = (T[]) new Object[size];
        this.size = size;
    }

    public void put(T element) {
        synchronized (putLock) {
            while (count >= size) {
                try {
                    notFull.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            queue[count++] = element;
            notEmpty.signal();
        }
    }

    public T take() {
        T element = null;
        synchronized (takeLock) {
            while (count == 0) {
                try {
                    notEmpty.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            element = queue[--count];
            queue[count] = null;
            notFull.signal();
        }
        return element;
    }
}

示例一:线程池中的任务队列实现

下面是一个示例,该示例演示如何在线程池中使用阻塞队列来实现任务队列的功能。该线程池实现 consist of 10 workers 和 一个阻塞队列,队列大小为5,当队列满时,任务生产者的添加操作会被阻塞。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        BlockingQueue<Runnable> taskQueue = new BlockingQueue<>(5);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new TaskProducer(taskQueue));
        }
        for (int i = 0; i < 5; i++) {
            executorService.execute(new TaskConsumer(taskQueue));
        }
    }

    static class TaskProducer implements Runnable {
        private BlockingQueue<Runnable> taskQueue;

        public TaskProducer(BlockingQueue<Runnable> taskQueue) {
            this.taskQueue = taskQueue;
        }

        @Override
        public void run() {
            while (true) {
                taskQueue.put(() -> System.out.println(Thread.currentThread().getName() + " is running."));
            }
        }
    }

    static class TaskConsumer implements Runnable {
        private BlockingQueue<Runnable> taskQueue;

        public TaskConsumer(BlockingQueue<Runnable> taskQueue) {
            this.taskQueue = taskQueue;
        }

        @Override
        public void run() {
            while (true) {
                Runnable task = taskQueue.take();
                task.run();
            }
        }
    }
}

示例二:生产者消费者问题实现

下面是一个示例,该示例演示如何使用阻塞队列来实现经典的生产者消费者问题。这个例子中,我们定义了一个存放字符的阻塞队列,由一个生产者线程不断往队列中添加字符,由多个消费者线程从队列中取出字符并打印。

public class ProducerConsumerDemo {
    public static void main(String[] args) {
        BlockingQueue<Character> blockingQueue = new BlockingQueue<>(5);
        Thread producerThread = new Thread(() -> {
            String str = "hello world";
            int len = str.length();
            for (int i = 0; i < len; i++) {
                blockingQueue.put(str.charAt(i));
            }
        });
        producerThread.start();
        Thread[] consumerThreads = new Thread[3];
        for (int i = 0; i < 3; i++) {
            Thread consumerThread = new Thread(() -> {
                while (true) {
                    Character c = blockingQueue.take();
                    System.out.println(Thread.currentThread().getName() + ": " + c);
                }
            });
            consumerThreads[i] = consumerThread;
            consumerThread.start();
        }
        try {
            producerThread.join();
            for (int i = 0; i < 3; i++) {
                consumerThreads[i].interrupt();
                consumerThreads[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

以上就是使用Java的wait和notifyAll实现简单的阻塞队列的完整攻略,希望对你有所帮助!

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java wait和notifyAll实现简单的阻塞队列 - Python技术站

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

相关文章

  • Java中字符串中连续相同字符去重方法

    在Java中,要去除字符中连续相同的字符,可以使用正则表达式或者递归的方式实现。以下是实现方法的详细攻略。 使用正则表达式 正则表达式是一种用于匹配字符串的模式,可以用来查找、替换和拆分字符串。Java中使用Pattern和Matcher两个类来进行正则表达式的匹配操作。 在去除字符串中连续相同的字符时,可以使用正则表达式来进行匹配和去重操作。具体步骤如下:…

    Java 2023年5月27日
    00
  • Java代码如何判断linux系统windows系统

    如果你需要编写能够跨平台执行的Java代码,就需要判断当前代码所运行的系统类型。Java提供了一些方法,可以方便地实现这个功能。 1. 使用System.getProperty()方法 System.getProperty()方法可以获取当前操作系统的相关信息,如:操作系统名称,操作系统版本和架构等。接下来,通过判断当前操作系统的名称来区分不同的操作系统。 …

    Java 2023年5月24日
    00
  • java实现服务器文件打包zip并下载的示例(边打包边下载)

    让我详细讲解“Java实现服务器文件打包zip并下载的示例(边打包边下载)”的完整攻略。 1. 准备工作 在开始操作之前,需要准备以下两件事情: 相应的Java开发环境; 一个Web服务器,如Tomcat。 2. 解压文件并创建Java项目 首先,需要从服务器中解压需要打包的文件。接下来,在Java项目中创建以下文件夹: src/main/java src/…

    Java 2023年5月19日
    00
  • Java8使用LocalDate计算日期实例代码解析

    Java8使用LocalDate计算日期实例代码解析 简介 Java8中新增了一个日期时间API–java.time包,其中一个类LocalDate可以用来处理日期。在这个攻略中,我们将通过两个示例代码详细介绍如何使用LocalDate计算日期。 示例1:计算两个日期相差的天数 import java.time.LocalDate; import java…

    Java 2023年5月20日
    00
  • Java快速排序与归并排序及基数排序图解示例

    Java快速排序与归并排序及基数排序图解示例 快速排序、归并排序和基数排序是算法中常用的排序方法,以下分别进行详细讲解。 快速排序 快速排序是一种分治算法,其基本思想是将一个大的数据序列分成两个小的数据序列。具体做法是通过递归实现的,在每次递归时选定一个基准数(通常选第一个或者最后一个数),将整个序列中小于基准数的数放在基准数左边,大于基准数的数放在基准数右…

    Java 2023年5月19日
    00
  • Java String类正则操作示例

    Java String类正则操作示例 简介 Java中String类提供了很多方法进行正则表达式的操作。通过使用正则表达式,我们可以在字符串中匹配特定的字符或者模式,进行替换或者搜索等操作。在这篇文章中,我们将学习String类操作正则表达式的方法,并且提供两个实际的示例说明。 String类操作正则表达式的方法 Java String类提供了以下方法来操作…

    Java 2023年5月27日
    00
  • java中使用数组进行模拟加密的方法

    Java中使用数组进行模拟加密的方法 对于小规模的数据加密,可以使用Java的数组进行模拟加密。具体实现方法如下: 首先,定义一个加密数组,用于模拟加密过程。例如: int[] encryptArray = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0}; 这个数组中的元素可以是0-9的任意数,用于表示加密后的数字。可以根据需要更改数组中的元素…

    Java 2023年5月26日
    00
  • SpringBoot Security密码加盐实例

    以下是“SpringBoot Security密码加盐实例”的完整攻略。 1. 密码加盐概述 密码加盐是一种常见的密码加密方式。通过将密码与随机字符串(盐)组合,使得相同密码在加密后生成的结果不同,增加破解难度。 2. 添加Spring Security依赖 在pom.xml文件中添加以下依赖: <dependency> <groupId&…

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