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

相关文章

  • python遍历小写英文字母的方法

    Sure! 下面是使用Python遍历小写英文字母的方法的完整攻略: import string # 方法一:使用for循环遍历 for letter in string.ascii_lowercase: print(letter) # 方法二:使用列表推导式生成字母列表 letters = [letter for letter in string.asci…

    other 2023年8月19日
    00
  • Ajax 动态载入html页面后不能执行其中的js快速解决方法

    当我们使用 Ajax 技术异步加载 HTML 页面时,如果此页面中包含 JavaScript 代码,那么默认情况下这些 JavaScript 代码是不会被执行的,因为异步请求的 HTML 页面并没有像普通页面那样被完全加载和渲染。为了解决这个问题,本文会给出一些常见的解决方法。 方法一:eval 函数 在 Ajax 获取到 HTML 页面后,我们可以通过 e…

    other 2023年6月27日
    00
  • golang快速入门:从菜鸟变大佬

    Golang快速入门:从菜鸟变大佬 简介 Go是一种开源的编程语言,由Google开发。它具有高效、简洁、安全等特点,适用于构建高性能的网络服务和分布式系统。本攻略将介绍如何快速入门Go语言,从菜鸟变成大佬。 安装和配置 可以从官方网站下载Go语言的安装包,并按照提示进行安装。安装完成后,需要配置环境变量,以便在命令行中使用Go命令。可以在.bashrc或.…

    other 2023年5月7日
    00
  • c++-在c++中将char转换为int

    在C++中将char类型转换为int类型的方法有多种,下面是两种常用的方法: 方法1:使用强制类型转换 可以使用强制类型转换将char类型转换为int。例如: char c = ‘a’; int i = (int)c; 在上面的示例中,将字符’a’赋值给变量c,然后使用强制类型转换将c转换为int类型,并将结果赋值给变量i。 方法2:使用ASCII码 在C+…

    other 2023年5月7日
    00
  • IDEA提示内存不足 low memory的完美解决方法(亲测好用)

    IDEA提示内存不足 low memory的完美解决方法(亲测好用) 当使用IntelliJ IDEA进行开发时,有时会遇到\”内存不足\”的提示,这可能会导致程序运行缓慢或崩溃。下面是一些解决这个问题的方法,经过亲测证明非常有效。 方法一:增加内存分配 打开IntelliJ IDEA,点击菜单栏中的\”Help\”(帮助)选项。 在下拉菜单中选择\”Edi…

    other 2023年8月2日
    00
  • Android中关于FragmentA嵌套FragmentB的问题

    Android中关于FragmentA嵌套FragmentB的问题攻略 在Android开发中,我们经常需要在一个Fragment中嵌套另一个Fragment。这种嵌套可以帮助我们构建复杂的用户界面和模块化的代码结构。下面是一个详细的攻略,介绍如何在Android中实现FragmentA嵌套FragmentB的功能。 步骤一:创建FragmentA和Frag…

    other 2023年7月28日
    00
  • Lua中的string库(字符串函数库)总结

    Lua中的string库(字符串函数库)提供了一系列常见的字符串操作函数,本篇攻略将对这些函数进行详细的讲解和总结。 一、字符串的基础操作 1. 字符串长度 字符串长度可以通过#运算符来获取,如下所示: local str = "hello world" print(#str) –> 11 2. 字符串连接 Lua中,使用..运算…

    other 2023年6月20日
    00
  • Element-ui upload上传文件限制的解决方法

    当使用 Element-ui 的 Upload 组件时,我们可能会遇到一些文件大小或文件数量的限制问题。这里提供一些解决这类问题的方法。 限制上传文件数量 我们可以使用 Element-ui 的 limit 属性来限制可以上传的文件数量。例如,以下代码将限制用户最多只能上传 3 个文件: <el-upload :limit="3" …

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