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

使用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日

相关文章

  • zgc介绍

    ZGC介绍 ZGC是一款由Oracle开发的垃圾回收器,专门用于处理大内存的Java应用程序。它采用了分代垃圾收算,可以在数毫秒内处理数百GB的内存。本文将详细介绍ZGC的特点、优势和使用方法,并提供两个示例说明。 特点 ZGC的主要特点如下: 低延迟:ZGC的垃圾回收时间通常超过10ms,可以不影响应用程序性能的情况下回收大内存。 大内存:ZGC可以处理数…

    other 2023年5月7日
    00
  • 6款实用的硬盘、ssd固态硬盘、u盘、储存卡磁盘性能测试工具

    6款实用的硬盘、SSD固态硬盘、U盘、储存卡磁盘性能测试工具 为了更好地测试硬盘、SSD固态硬盘、U盘和储存卡等存储设备的性能,提高存储设备的管理效率,有必要了解一些性能测试工具。本文将介绍6款实用的硬盘、SSD固态硬盘、U盘和储存卡磁盘性能测试工具。 1. Crystaldiskmark Crystaldiskmark是一款用于测试硬盘和SSD固态硬盘性能…

    其他 2023年3月28日
    00
  • Notepad++字符空行替换技巧四则新手进阶

    Notepad++字符空行替换技巧四则新手进阶攻略 Notepad++是一款功能强大的文本编辑器,提供了许多实用的功能,其中字符空行替换技巧是新手进阶的重要一环。本攻略将详细介绍如何使用Notepad++进行字符空行替换,并提供两个示例说明。 步骤一:打开Notepad++ 首先,确保你已经安装了最新版本的Notepad++。然后,打开Notepad++编辑…

    other 2023年8月18日
    00
  • Mysql账号管理与引擎相关功能实现流程

    MySQL是一种关系型数据库管理系统,是很多网站和应用程序后台的首选数据库系统之一。MySQL账号管理和引擎相关功能对确保MySQL数据库的安全性有着至关重要的作用。下面,我将详细讲解MySQL账号管理和引擎相关功能的实现流程。 Mysql账号管理 创建新用户 要创建一个新用户,可以使用以下语句: CREATE USER ‘newuser’@’localho…

    other 2023年6月27日
    00
  • 实用的portraiture滤镜磨皮教程

    实用的 Portraiture 滤镜磨皮教程 概述 在肖像摄影中,皮肤质感和肤色是至关重要的元素。为了达到一张完美的肖像照片,很多摄影爱好者会使用磨皮技术。这种技术可以让照片中的皮肤更加光滑,减少皱纹和瑕疵,但如果使用不当,也容易让人物显得不真实。Portraiture 滤镜将为您的肖像照片提供最佳磨皮效果,同时保持自然的外观。 这篇教程将介绍使用Portr…

    其他 2023年3月28日
    00
  • SQL Server Reporting Services 匿名登录的问题及解决方案

    实现SSRS匿名登录是一项常见的需求,但它涉及到一些默认安全限制,因此需要掌握一些技巧来处理。本文将详细讲解匿名登录的问题及解决方案。 1. 问题描述 在默认情况下,SSRS报表需要认证用户才能访问报表,这意味着,无论在本地还是在远程环境中,用户必须提供正确的凭据才能访问报表。而有些情况下,我们希望用户能够匿名访问报表而无需提供凭据。如果您尝试访问报表服务器…

    other 2023年6月26日
    00
  • 最小人工智能硬件资源jetsonnanovs树莓派4b

    以下是关于“最小人工智能硬件资源Jetson Nano和树莓派4B”的完整攻略,包含两个示例。 Jetson Nano和树莓派4B Jetson Nano和树莓派4B都是流行的最小人工智能硬件资源,它们都可以用于开发和运行人工智能应用程序。以下是关于Jetson Nano和树莓派4B的详细攻略。 1. Jetson Nano Jetson Nano是一款由N…

    other 2023年5月9日
    00
  • 解决svn每次操作都需要重输入用户名密码问题

    解决 SVN 每次操作都需要重新输入用户名密码问题 如果你经常使用 SVN 进行代码的版本控制,你可能会遇到每次对版本库进行操作都需要重新输入用户名密码的问题。这个问题可能会让你感到很困扰,因为每次都需要输入用户名和密码会导致你的工作效率降低。 这个问题的主要原因是 SVN 默认不会缓存用户的用户名和密码,每次使用 SVN 都需要重新输入。但是,SVN 提供…

    其他 2023年3月29日
    00
合作推广
合作推广
分享本页
返回顶部