【JavaSE】【多线程】阻塞队列

【JavaSE】【多线程】阻塞队列

目录

一、阻塞队列

阻塞队列:是一种特殊的队列,也有先进先出的特性。它是一种线程安全的队列。
有以下两个特性:

  • 当队列的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素。
  • 当队列的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素。
    阻塞队列的一个重要应用场景就是:实现生产者消费模型。

1.1 生产者消费者模型

生产者消费者模型:是多线程编程中的一种典型的编码技巧。用来降低生产者与消费者之间的耦合度。生产者和消费者之间的交易场所就是一个阻塞队列。

这样的模型的优势有以下两个:

  1. 解耦合,降低代码耦合度:
    像如果是A B两个服务器,之间直接进行交互,如果对A或者B中的数据进行修改操作,大概率就会影响到另一个服务器。而使用阻塞队列作为交易平台,我们修改服务器的数据时,由于阻塞队列中的结构固定,两个服务器之间的耦合度就降低。
  2. 削峰削谷:
    在服务器中,波峰就是请求量高的时候,波谷就是请求量低的时候。
    如果是AB两个服务器之间进行交互,当上游服务器A经历波峰,将大量请求传给服务器B的时候,服务器就有可能挂掉。
    因为上游服务器,干的活简单,消耗的资源少;而下游服务器,干的活复杂,消耗的的资源就多。
    但是如果我们将阻塞队列作为交易平台,那么服务器B就可以依据自己的节奏从队列中拿请求。

但是这样的模型也会付出代价:

  1. 引入阻塞队列之后整体结构会更加复杂。比如本来是AB两个服务器之间的交互,但引入一个作为阻塞队列的服务器(这种称为消息队列),就需要部署这个服务器,还要与AB实现交互。
  2. 效率也会有影响。

1.2 Java提供的阻塞队列

提供了一个BlockingDeque的接口(需要导java.util.concurrent.BlockingQueue包):

主要使用下面3个实现了BlockingDeque接口的来实例化阻塞队列:

小根堆实现的,PriorityBlockingDeque需要导java.util.concurrent.PriorityBlockingDeque包):

数组实现的,ArrayBlockingDeque需要导`java.util.concurrent.ArrayBlockingDeque包):

链表实现的,LinkedBlockingDeque(需要导java.util.concurrent.LinkedBlockingDeque包):

在阻塞队列中我们虽然可以使用队列中常用的出队列入队列方法,但是那些方法不带阻塞效果。带阻塞效果的入队列方法是put,出队列方法是take,这两个方法都会抛出InterruptedException异常。

1.3 实现一个简单生产者消费者模型

实现一个简单的生产者消费者模型:

importjava.util.concurrent.BlockingQueue;importjava.util.concurrent.LinkedBlockingDeque;publicclassDemo{publicstaticvoidmain(String[] args){BlockingQueue<Integer> blockingQueue =newLinkedBlockingDeque<>(1000);Thread producer =newThread(()->{int i =0;while(true){try{ blockingQueue.put(i++);System.out.println(i +"入队列成功");}catch(InterruptedException e){ e.printStackTrace();}}});Thread consumer =newThread(()->{while(true){try{int x = blockingQueue.take();System.out.println(x +"出队列成功");}catch(InterruptedException e){ e.printStackTrace();}}}); producer.start(); consumer.start();}}

二、自己实现阻塞队列

我们使用数组来实现一个循环队列。
不知道循环队列的实现的可以看下面这个链接:队列

2.1 成员变量

  • 使用capacity代表数组的最大长度;
  • 使用size表示数组中元素的个数;
  • head表示队头元素的下标;
  • tail表示队尾元素的下标。
privateint capacity =0xffff;privateString[] elem ;//存储数组privateint size;//存储元素个数privateint head;//队头privateint tail;//队尾

2.2 构造方法

提供两个构造方法:

  • 使用默认最大值初始化数组;
  • 使用传的参初始化数组。
publicMyBlockingQueue(int capacity){this.capacity = capacity; elem =newString[this.capacity];}publicMyBlockingQueue(){ elem =newString[this.capacity];}

2.3 put方法

由于put和take方法都涉及到修改判断等操作,为避免原子性问题带来线程安全问题对该这些操作都要加锁。

在put方法中我们需要在队列满的时候发生阻塞,使用wait来等待。而在Java官方文档给出了建议我们使用循环语句来使用wait。

因为wait是除了notify唤醒外,还有可能被interrupt方法唤醒抛出异常,如果只要if,不用while,抛出异常后就会继续执行下面的逻辑,带来bug。而使用循环就不会,抛出异常后,会再次判断循环条件。

最后在入队成功后发出一个通知notify来唤醒由于队列空而阻塞等待的线程。

publicvoidput(String s)throwsInterruptedException{synchronized(this){while(size == elem.length){this.wait();} elem[tail]= s; tail =(tail+1)% elem.length; size++;this.notify();}}

2.4 take方法

当队列为空的时候,跟put一样使用wait来阻塞。
最后在出队成功后发出一个通知notify来唤醒由于队列满而阻塞等待的线程。

publicStringtake()throwsInterruptedException{synchronized(this){while(size ==0){this.wait();}String ret = elem[head]; head =(head+1)% capacity; size--;this.notify();return ret;}}

2.5 最终代码

最终我们自己实现的一个简单的阻塞队列就如下:

publicclassMyBlockingQueue{privateint capacity =0xffff;privateString[] elem ;//存储数组privateint size;//存储元素个数privateint head;//队头privateint tail;//队尾publicMyBlockingQueue(int length){ elem =newString[length];}publicMyBlockingQueue(){ elem =newString[this.capacity];}publicvoidput(String s)throwsInterruptedException{synchronized(this){while(size == elem.length){this.wait();} elem[tail]= s; tail =(tail+1)% capacity; size++;this.notify();}}publicStringtake()throwsInterruptedException{synchronized(this){while(size ==0){this.wait();}String ret = elem[head]; head =(head+1)% capacity; size--;this.notify();return ret;}}}

Read more

Rust嵌入式开发实战——从ARM裸机编程到RTOS应用

Rust嵌入式开发实战——从ARM裸机编程到RTOS应用

Rust嵌入式开发实战——从ARM裸机编程到RTOS应用 一、学习目标与重点 1.1 学习目标 1. 理解嵌入式开发基础:深入掌握嵌入式系统的定义、特点、架构(ARM、RISC-V),对比Rust与传统嵌入式开发语言(C/C++)的优势 2. 搭建Rust嵌入式开发环境:安装交叉编译工具链(arm-none-eabi、riscv64-unknown-elf)、调试工具(OpenOCD、GDB),配置VS Code/CLion开发环境 3. 掌握Rust裸机编程:使用cortex-m、cortex-m-rt库进行ARM裸机开发,实现GPIO操作、串口通信、中断处理 4. 学习RTOS开发:使用RTIC(Real-Time Interrupt-driven Concurrency)实现多任务编程,理解任务调度、资源共享、中断管理 5. 实战嵌入式项目:结合STM32F4xx系列开发板、Raspberry

By Ne0inhk

Spring IoC&DI

目录 1.Spring是什么? 2.详解Ioc 2.1Bean的存储 2.2 Bean Name默认命名规则 2.3 扫描路径 2.4@Autowired和@Resource区别 2.5 @Autowired查找Bean顺序 3.详解DI Spring三种依赖注入方式优缺点对比 1.Spring是什么? Spring 是一个开源的 Java 开发框架,核心目标是简化企业级应用开发。它提供了模块化工具(如 Spring MVC、Spring Security、Spring Data 等),让开发者更专注于业务逻辑,而非重复的底层代码。也就是包含了众多方法的IoC容器, 容器是什么? 在 Spring 中,容器是一个 装对象的“盒子”

By Ne0inhk
Rust微服务架构实战——gRPC通信、服务发现与容器编排

Rust微服务架构实战——gRPC通信、服务发现与容器编排

第12篇:Rust微服务架构实战——gRPC通信、服务发现与容器编排 一、学习目标与重点 1.1 学习目标 1. 理解微服务架构:深入学习微服务的核心概念、优缺点、架构模式,掌握微服务与单体架构的区别 2. 掌握gRPC通信:熟练使用Tonic(Rust的gRPC实现)定义.proto文件、生成服务端和客户端代码,实现同步/异步通信 3. 实现服务发现与负载均衡:使用Consul或etcd实现服务注册与发现,使用Ribbon或Nginx实现负载均衡 4. 容器编排与部署:学习Docker Swarm或Kubernetes的核心概念,使用Docker Compose或Kubernetes YAML文件部署微服务 5. 实战微服务开发:结合真实场景编写用户管理、订单管理、支付管理三个微服务,实现gRPC通信、服务发现、负载均衡 6. 监控与运维:使用Prometheus+Grafana监控微服务,使用ELK Stack收集和分析日志 1.

By Ne0inhk
OpenClaw 架构深度拆解:工程优雅的本地优先 AI Agent,为何难入企业级生产环境?

OpenClaw 架构深度拆解:工程优雅的本地优先 AI Agent,为何难入企业级生产环境?

2026 年,AI Agent 赛道早已从概念炒作进入工程化落地的深水区。无数项目沉迷于堆功能、炒概念,把 Agent 做成了花里胡哨的聊天玩具,却始终解决不了最核心的问题:执行不可靠、状态不可控、结果不可复现。而近期开源的 OpenClaw,却以一套极简、清晰、职责分离的分层架构,成为了业内公认的 “最干净的 Agent 运行时” 参考设计。 它以本地优先为核心理念,在工程层面做出了极佳的示范,解决了当前绝大多数 Agent 框架普遍存在的竞态 bug、上下文溢出、执行混乱等痛点;但与此同时,它的执行模型也带来了巨大的安全攻击面,在企业级场景的安全与治理上,存在致命的短板。 本文将从核心定位、五层架构全拆解、工程设计亮点、企业级安全短板、实践启示五个维度,深度解析这个本地优先的 AI Agent 系统,帮你吃透它的设计精髓,同时规避落地过程中的安全风险。 一、OpenClaw 的核心定位:

By Ne0inhk