利用Java手写阻塞队列的示例代码

yizhihongxing

使用Java手写阻塞队列是一种常见的并发编程技巧。这在许多场合下非常有用,例如当多个线程需要访问共享资源时,或者需要实现生产者-消费者模型时。下面是手写阻塞队列示例代码及其解释:

步骤1:定义接口

interface CustomBlockingQueue<T> {
    void put(T item) throws InterruptedException;
    T take() throws InterruptedException;
}

这里定义了一个泛型接口CustomBlockingQueue来表示阻塞队列。它包含两个方法:puttake。阻塞队列的生产者使用put方法向队列中添加元素,在队列已满时会阻塞等待;而消费者使用take方法从队列中获取元素,在队列为空时会阻塞等待。这样可以防止生产者向队列中添加元素过多导致队列溢出,或者消费者从队列中获取元素过多导致队列为空。

步骤2:实现接口

class CustomArrayBlockingQueue<T> implements CustomBlockingQueue<T> {
    private final T[] items;
    private volatile int head, tail;
    private final Object notEmpty = new Object();
    private final Object notFull = new Object();

    @SuppressWarnings("unchecked")
    public CustomArrayBlockingQueue(int maxSize) {
        items = (T[]) new Object[maxSize];
    }

    public void put(T item) throws InterruptedException {
        while (isFull()) {
            synchronized (notFull) {
                notFull.wait();
            }
        }

        items[tail] = item;
        if (++tail == items.length) {
            tail = 0;
        }

        synchronized (notEmpty) {
            notEmpty.notify();
        }
    }

    public T take() throws InterruptedException {
        while (isEmpty()) {
            synchronized (notEmpty) {
                notEmpty.wait();
            }
        }

        T item = items[head];
        items[head] = null;
        if (++head == items.length) {
            head = 0;
        }

        synchronized (notFull) {
            notFull.notify();
        }

        return item;
    }

    private boolean isEmpty() {
        return head == tail && items[head] == null;
    }

    private boolean isFull() {
        return head == tail && items[head] != null;
    }
}

上面的代码实现了CustomBlockingQueue接口定义的两个方法:puttake。它使用了Object 类型的锁和waitnotify方法来实现线程的等待和唤醒。队列容量大小是有限的,由构造函数参数maxSize指定。当队列已满时,put方法将会等待直到队列空闲;而当队列为空时,take方法将会等待直到队列非空。队列的头末指针headtail用来指示当前队列状态,head指向队列的头部,tail指向队列的尾部。当headtail相等时,队列可能为空或者已满,需要进行判断。put和take方法在结束时都会唤醒可能等待的线程。

示例1:多个生产者和消费者共享阻塞队列

下面的代码示例说明了如何使用上面实现的阻塞队列来实现多个生产者和消费者共享一个队列。

假设我们有两个生产者和两个消费者:Producer1Producer2Consumer1Consumer2。我们让所有生产者和消费者共享一个阻塞队列。

public class SharedBlockingQueueExample {
    public static void main(String[] args) {
        CustomBlockingQueue<String> queue = new CustomArrayBlockingQueue<>(20);

        Producer producer1 = new Producer(queue, "Producer1");
        Producer producer2 = new Producer(queue, "Producer2");
        Consumer consumer1 = new Consumer(queue, "Consumer1");
        Consumer consumer2 = new Consumer(queue, "Consumer2");

        producer1.start();
        producer2.start();
        consumer1.start();
        consumer2.start();
    }
}

生产者线程和消费者线程的实现如下:

class Producer extends Thread {
    private final CustomBlockingQueue<String> queue;
    private final String name;

    public Producer(CustomBlockingQueue<String> queue, String name) {
        this.queue = queue;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            for (int i = 1; i <= 100; i++) {
                String item = "item " + i + " from " + name;
                queue.put(item);
                System.out.println("put " + item);
                sleep(new Random().nextInt(100));
            }
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
    }
}

class Consumer extends Thread {
    private final CustomBlockingQueue<String> queue;
    private final String name;

    public Consumer(CustomBlockingQueue<String> queue, String name) {
        this.queue = queue;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            while (true) {
                String item = queue.take();
                System.out.println("take " + item + " by " + name);
                sleep(new Random().nextInt(100));
            }
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
    }
}

运行这个程序,我们会看到所有的生产者和消费者不断地生产和消费元素,并且队列的容量保持在20个以内。如果队列已满,则生产者线程会被阻塞等待;如果队列为空,则消费者线程会被阻塞等待。这确保了队列中的元素数量不会超过我们设定的容量。

示例2:阻塞队列实现生产者-消费者模型

下面的代码示例说明了如何使用上面实现的阻塞队列来实现生产者-消费者模型。

假设我们有一个生产者线程和一个消费者线程,它们共享一个阻塞队列,生产者线程不断地向队列中添加元素,消费者线程不断地从队列中获取元素。我们设置队列容量为10,一旦队列已满则生产者线程将会被阻塞等待;一旦队列为空则消费者线程将会被阻塞等待。

public class ProducerConsumerExample {
    public static void main(String[] args) {
        CustomBlockingQueue<Integer> queue = new CustomArrayBlockingQueue<>(10);

        ProducerThread producer = new ProducerThread(queue);
        ConsumerThread consumer = new ConsumerThread(queue);

        producer.start();
        consumer.start();
    }
}

class ProducerThread extends Thread {
    private final CustomBlockingQueue<Integer> queue;

    ProducerThread(CustomBlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i <= 50; i++) {
                queue.put(i);
                System.out.println("Produced: " + i);
            }
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
    }
}

class ConsumerThread extends Thread {
    private final CustomBlockingQueue<Integer> queue;

    ConsumerThread(CustomBlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                Integer item = queue.take();
                System.out.println("Consumed: " + item);
            }
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
    }
}

在此示例程序中,我们生成了50个数字并向阻塞队列中添加。由于队列的容量为10,当队列已满时,生产者线程将会被阻塞等待,直到队列有空闲位置。在消费者线程中,我们不断地从队列中获取元素,如果队列为空,则消费者线程会被阻塞等待,直到队列非空。

总结

实现阻塞队列是一项非常重要的并发编程技巧。在实际开发中,我们通常会使用Java内置的阻塞队列实现(如java.util.concurrent.ArrayBlockingQueuejava.util.concurrent.LinkedBlockingQueue等)。手写的阻塞队列实现更多地是一种理论上的练习。无论使用哪种方法,阻塞队列的基本概念和操作方法都是类似的。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:利用Java手写阻塞队列的示例代码 - Python技术站

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

相关文章

  • 电脑开机无图标 显示svchost.exe应用程序错误该怎么办?

    如果电脑开机后没有出现图标,而且出现了 svchost.exe 应用程序错误的提示,应该按照以下步骤进行排查和处理: 检查症状 首先要确认电脑确实出现了“电脑开机无图标 显示svchost.exe应用程序错误”的症状,可以观察电脑开机的过程中屏幕上是否出现了错误提示框,以及是否能够进入系统界面。 检查硬件 如果电脑的硬件出现了问题,可能导致出现该症状。可以检…

    other 2023年6月25日
    00
  • Android AndFix热修复原理详情

    Android AndFix 热修复原理详情 什么是热修复? 热修复指的是在应用运行过程中,对已经发布的APK进行局部修改和更新。通过热修复技术,可以在无需重新打包和升级应用的情况下,快速修复应用出现的问题。 为什么需要热修复? 应用在运营过程中,难免会出现一些需要修复的Bug和安全漏洞。如果要通过重新发布APK的方式进行修复,需要等待应用市场的审核与审核通…

    other 2023年6月25日
    00
  • 函数flst_init

    函数flst_init 函数flst_init是一种图像处理算法中的函数,用于对图像进行分割处理。本攻略将详细讲解函数flst_init的原理、实现方法和示例说明。 原理 函数flst_init的原理是对图像进行分割处理,将图像中的每个像素分配到不同的区域中。具体来说,函数flst_init可以分为以下几步骤: 1.图像进行预处理,如去噪、增强等。 对图像进…

    other 2023年5月7日
    00
  • DOTNETBAR制作圆角窗体和圆角控件代码实例

    首先,我们需要了解什么是DotNetBar。DotNetBar是一个用于Windows.Forms应用程序的控件库,它提供了一系列美观、容易使用的控件和工具栏,并支持自定义皮肤、打印和报表、图像处理、XML等。它由 DevComponents 公司开发并维护。 接下来,我们将详细讲解如何使用DotNetBar制作圆角窗体和圆角控件。 制作圆角窗体 1. 创建…

    other 2023年6月26日
    00
  • 快速解决低版本Xcode不支持高版本iOS真机调试的问题方法

    快速解决低版本Xcode不支持高版本iOS真机调试的问题方法攻略 如果你的Xcode版本较低,无法支持高版本iOS设备的真机调试,下面是一些解决该问题的方法。 方法一:使用适配工具 有一些第三方工具可以帮助你在低版本的Xcode上进行高版本iOS设备的真机调试。其中一个常用的工具是 ios-deploy。以下是使用该工具的步骤: 打开终端(Terminal)…

    other 2023年8月3日
    00
  • Redis优惠券秒杀企业实战

    Redis优惠券秒杀企业实战 本文将分享Redis优惠券秒杀的完整攻略,包括Redis的基础知识、秒杀实现原理、业务流程以及代码实现。通过学习本篇文章,读者可深入了解Redis优惠券秒杀的相关知识,为实战落地提供指导作用。 Redis的基础知识 Redis是一种高性能的键值存储数据库,它可以存储字符串、整数、浮点数、列表、哈希表、集合等多种数据类型。Redi…

    other 2023年6月26日
    00
  • python通过配置文件共享全局变量的实例

    Python通过配置文件共享全局变量的实例攻略 在Python中,可以通过配置文件来共享全局变量。这种方法可以让我们在不修改代码的情况下,根据需要动态地改变全局变量的值。下面是一个详细的攻略,包含了两个示例说明。 步骤1:创建配置文件 首先,我们需要创建一个配置文件,用于存储全局变量的值。配置文件可以使用常见的格式,如INI、JSON或YAML。在这个攻略中…

    other 2023年7月28日
    00
  • 批处理命令Start的使用介绍

    批处理命令Start的使用介绍 start 命令是 Windows 操作系统中的一个批处理命令,可以启动一个新的进程,也就是在另一个命令窗口中运行指定的程序或命令。 命令格式 start ["title"] [/d path] [/i] [/min] [/max] [/separate | /shared] [/low | /normal…

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