Java并发编程之阻塞队列(BlockingQueue)详解

Java并发编程之阻塞队列(BlockingQueue)详解

什么是阻塞队列?

阻塞队列,顾名思义就是在队列的基础上加入了阻塞的特性。当队列满时,阻塞队列会自动阻塞写入线程,直到队列中有元素被移除,而当队列为空时,阻塞队列会自动阻塞读取线程,直到队列中有元素被添加。

Java中的阻塞队列是一个线程安全的队列,实现了如同锁的机制,可以保证多个线程同时访问是安全的,并且提供了多种阻塞方式,适合于不同的业务场景。

阻塞队列的分类

Java中提供了多种不同类型的阻塞队列,常用的有以下几种:

ArrayBlockingQueue

ArrayBlockingQueue是一个由数组构成的有界阻塞队列,队列的容量在创建的时候就固定了,对元素的添加和移除都会阻塞线程,直到成功为止。

可以使用以下方式创建一个ArrayBlockingQueue:

BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);

LinkedBlockingQueue

LinkedBlockingQueue是一个由链表构成的有界或无界阻塞队列,如果队列容量没有限制,则对元素的添加不会阻塞线程(除非内存不足),而对元素的移除会阻塞线程,直到队列中有元素被添加。

可以使用以下方式创建一个LinkedBlockingQueue:

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

SynchronousQueue

SynchronousQueue是一个没有容量的阻塞队列,对元素的添加和移除都会阻塞线程,直到另外一个线程正在等待接收被添加的元素为止。

可以使用以下方式创建一个SynchronousQueue:

BlockingQueue<String> queue = new SynchronousQueue<>();

阻塞队列的基本操作

put方法

put方法会将指定元素插入到队列的尾部并阻塞线程,直到成功添加为止。如果队列已经满了,则线程会一直阻塞。

BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
queue.put("hello");

take方法

take方法会将队列的头部元素移除并返回,如果队列为空,则该方法会一直阻塞线程,直到队列中有元素被添加。

BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
String str = queue.take();

示例

生产者消费者模式

下面使用阻塞队列来实现一个简单的生产者消费者模式。假设有两个线程,一个线程负责生产数据并将数据放入阻塞队列中,另一个线程负责从阻塞队列中取数据并进行消费。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
    }

    static class Producer implements Runnable {

        private BlockingQueue<String> queue;

        public Producer(BlockingQueue<String> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    String data = "data-" + i;
                    queue.put(data);
                    System.out.println(Thread.currentThread().getName() + " 生产了 " + data);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class Consumer implements Runnable {

        private BlockingQueue<String> queue;

        public Consumer(BlockingQueue<String> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    String data = queue.take();
                    System.out.println(Thread.currentThread().getName() + " 消费了 " + data);
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

在上面的示例中,Producer线程会不断地生产数据并将数据放入阻塞队列中,而Consumer线程则会不断地从阻塞队列中取数据并进行消费。由于阻塞队列的阻塞特性,当队列满了时,生产者线程会被阻塞,直到队列中有数据被消费;当队列为空时,消费者线程会被阻塞,直到队列中有数据被添加。

限流

下面使用阻塞队列来实现一个限流的场景。假设有一个API接口,限制每秒钟只能处理10个请求,超过10个请求的需要被丢弃。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class RateLimitDemo {

    private static int MAX_QPS = 10;

    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(MAX_QPS);
        ExecutorService executor = Executors.newCachedThreadPool();

        // 开启10个线程不断往队列中添加请求
        for (int i = 0; i < MAX_QPS; i++) {
            executor.execute(() -> {
                while (true) {
                    try {
                        queue.put("request");
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        // 开启一个线程处理请求
        executor.execute(() -> {
            while (true) {
                try {
                    // 从队列中取出请求
                    String request = queue.take();
                    System.out.println("正在处理请求:" + request);
                    TimeUnit.MILLISECONDS.sleep(100); // 模拟处理时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

}

在上面的示例中,首先我们创建了一个容量为10的阻塞队列,并开启了10个线程不断地往队列中添加请求。由于队列的容量为10,因此最多可以处理10个请求。同时,我们开启了一个线程来处理请求,该线程会不断地从队列中取出请求并进行处理。由于队列的阻塞特性,如果队列已经满了(即已经有10个请求等待处理),新来的请求会阻塞线程,直到队列中有请求被处理。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java并发编程之阻塞队列(BlockingQueue)详解 - Python技术站

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

相关文章

  • 关于Java8 parallelStream并发安全的深入讲解

    关于Java8 parallelStream并发安全的深入讲解 Java 8引入的Stream API提供了一种非常方便和高效的处理集合的方式。parallelStream()方法可以使用多线程来利用CPU的多核执行计算。本文将深入讲解Java 8中parallelStream()的实现原理以及如何保证并发安全。 parallelStream() 并行流的实…

    多线程 2023年5月16日
    00
  • Android版多线程下载 仿下载助手(最新)

    下面是《Android版多线程下载 仿下载助手(最新)》的完整攻略。 一、项目说明 本项目为 Android 版本多线程下载,实现了仿照下载助手的功能,支持多线程下载、暂停和继续下载、断点续传、下载速度统计等等。 二、环境配置 首先,我们需要安装以下环境: JDK Android Studio Git 三、下载源码 我们可以在 GitHub 上将项目克隆到本…

    多线程 2023年5月16日
    00
  • 详解Java中的线程模型与线程调度

    详解Java中的线程模型与线程调度 线程模型 在Java中,线程的实现是基于OS的线程(Native Thread)实现的。每个Java线程对应了一个OS线程。 线程模型主要由执行线程和阻塞线程两部分组成。执行线程就是正在执行的线程,阻塞线程就是等待某些事件或条件才能执行的线程。 当线程遇到IO或者锁时,线程进入阻塞状态,被加入到对应的阻塞队列中。等待IO或…

    多线程 2023年5月17日
    00
  • Java实现多线程大批量同步数据(分页)

    Java实现多线程大批量同步数据(分页)攻略 简述 在处理大批量数据同步的场景下,采用多线程可以有效提高数据同步效率。而在数据分页的情况下,也需要实现多线程分页同步。本文将介绍如何使用Java实现多线程大批量同步数据(分页)的完整攻略。 思路 实现多线程大批量同步数据(分页)的思路如下: 将需要分页同步的数据按照分页大小进行分割,每个分页开启一个线程进行同步…

    多线程 2023年5月16日
    00
  • Javaweb应用使用限流处理大量的并发请求详解

    Javaweb 应用使用限流处理大量的并发请求详解 在高并发情况下,大量的请求可能会造成服务器的宕机或响应延迟。为了解决这个问题,我们可以使用限流的方法来平滑控制请求的流量和数量。 什么是限流 限流是指在某种情况下控制流量或者节流保持并发线程的数量在合理的范围之内。在实际应用中,限流就是对某种资源或者连接、把它的使用量限制在一定范围内,防止由于某些原因导致的…

    多线程 2023年5月16日
    00
  • Golang极简入门教程(三):并发支持

    Golang极简入门教程(三):并发支持 什么是并发 并发是多个任务在同一时间间隔内同时执行的能力。在计算机中,使用线程和进程实现并发。 多线程和多进程 在计算机中,我们可以同时使用多线程和多进程来实现并发。 多线程: 操作系统会创建多个线程,每个线程可以执行不同的任务,这些任务会同时运行。这样可以提高程序的性能,避免单线程运行的资源浪费问题。同时,线程之间…

    多线程 2023年5月17日
    00
  • Go语言中的并发goroutine底层原理

    Go语言中的并发goroutine底层原理 背景 Go语言被称为互联网时代的C语言,因为它具有高效的并发能力,支持使用轻量级的goroutine进行并发编程。在Go语言中,每个goroutine都代表着一个独立的线程,但是它们可以在同一时间运行且共享内存,因此能够实现高效的并发编程。 goroutine的实现原理 Go语言的goroutine是基于M:N线程…

    多线程 2023年5月17日
    00
  • 彻底搞懂Java多线程(二)

    下面详细讲解一下“彻底搞懂Java多线程(二)”的完整攻略。 1. 线程的基本操作 在Java中,线程是通过Thread类来创建和启动的。创建线程的过程就是创建一个Thread对象,然后通过调用该对象的start()方法来启动线程,如下所示: Thread thread = new Thread(); thread.start(); 默认情况下,新线程会与当…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部