mq - 阻塞队列BlockingQueue
队列
Queue
在 java 中,继承自 Collection,多数的实现类位于 java.util.concurrent,与线程池位于同一个包下,大多数的队列都与线程和锁挂钩,少量位于 java.util,比如 LinkedList、PriorityQueue。
程序员最早接触的队列一般是 LinkedList,其实它不仅是一个 List,同时也是一个 Queue(也是双端队列),但是它并不是一个线程安全的 Queue,不适合在多线程访问下使用。
阻塞队列
BlockingQueue 是一个接口,BlockingQueue 直译就是阻塞队列,阻塞队列的实现类是线程安全的,常用于 “消息生产发布” 场景。
应用场景:定义一个队列,多个线程负责添加数据,多个线程从中获取数据,所有线程都是同时执行的,如果集合已满,则生产者等待,如果集合为空,那么消费者等待(实际就是经典的 "生产者/消费者" 场景了),
对程序员的要求偏高,一般不会直接使用,结合多线程的场景,一般会直接使用 ThreadPoolTaskExecutor 等类似工具。
队列(Queue)常用函数
- add(e):添加元素;
- offer(e):有容量限制的时候,优先使用此函数;与 add() 相比,超出容量时,不会抛出异常,而是返回 false;
- remove():删除第一个元素,何为第一个取决于数据结构;
- poll():删除第一个元素,与 remove() 相比,集合为空时,不会抛出异常,而是返回 null;
- element():获取第一个元素;
- peek():获取第一个元素,与 element() 相比,没有元素时,不会抛出异常,而是返回 null;
堆栈(Stack)常用函数
(jdk 中似乎没有严格意义上的堆栈,堆栈的方法定义在双端队列(Deque)接口中)
- push():插入到第一个元素;
- pop():从尾端获取最后一个元素; |
ACK 机制
业务场景:
- 队列中存在多种数据,处理线程需要确认数据类型,之后再正式取出数据;
- 处理线程有意外关闭的可能,需要将数据退回队列,因此要等处理结束的时候,再正式取出数据;
解决方案:
element() 和 peek() 用于数据检测,仅仅只是取值,但是不删除元素,通常用于一些元素的校验。
(这个机制以后还会在消息队列中看到)
几个简单的队列
- SynchronousQueue 译为同步队列,它也是一个阻塞队列。在代码的实现上,只允许存放一个元素,当存放第二个元素的时,线程进入等待,直到第一个元素被取出。
- ArrayBlockingQueue 定长数组阻塞队列,内部由数组实现,因为队列需要经常对数据进行存取、排序,其效率反而要低于链表阻塞队列,使用的时候必须指定长度。
- LinkedBlockingQueue 线性阻塞队列,内部由链表实现,不设置长度的情况,队列长度为Integer.MAX_VALUE,为防止队列过度扩展可以选择带参数的构造函数,其存取效率高于数组队列。
简单的消息轮询
/**
* 消息处理接口回调
*
* @author ChenSS on 2018年1月30日
*/
public interface Handler<T> {
void handleMessage(T msg) throws Exception;
void handleException(T msg, Exception e);
}
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* 阻塞队列消息轮询
*
* @author ChenSS on 2018年1月30日
*/
public class BlockingLopper<T> {
private BlockingQueue<T> queue;
/**
* 哑元元素,在不强制中断线程的情况下,用于告知通知轮询线程可以结束循环
*/
public T dummy;
private Handler<T> handler;
public void setQueue(BlockingQueue<T> queue) {
this.queue = queue;
}
public void setDummy(T dummy) {
this.dummy = dummy;
}
public void setHandler(Handler<T> handler) {
this.handler = handler;
}
/**
* 发送消息
*/
public void put(T value) throws InterruptedException {
queue.put(value);
}
/**
* 发送消息
*
* @throws IllegalStateException
* 通常表示队列已满,不允许添加新的元素
*/
public void add(T value) {
queue.add(value);
}
/**
* 发送消息
*
* @throws RuntimeException
* 代码存在部分未检查异常
*/
public boolean offer(T value) {
if (!queue.offer(value)) {
try {
// 简单的失败处理策略,当前线程等待0.5秒,重新尝试添加该元素
// 以实际开发为准,在非必要的情况不要阻塞生产线程
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.offer(value);
}
return true;
}
/**
* 发送消息
*
* @throws InterruptedException
* 等待时被中断
*/
public void offer(T value, long timeout) throws InterruptedException {
queue.offer(value, timeout, TimeUnit.MILLISECONDS);
}
/**
* 放置哑元元素,通知线程结束(取出所有元素后结束)
*/
public void stopLoop() throws InterruptedException {
queue.put(this.dummy);
}
/**
* 启动线程,开始轮询消息
*/
public Thread loop() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
T t = null;
for (;;) {
try {
t = queue.take();
if (dummy.equals(t)) {
return;
} else {
handler.handleMessage(t);
}
} catch (Exception e) {
handler.handleException(t, e);
}
}
}
});
thread.start();
return thread;
}
/**
* 启动守护线程,开始轮询消息
*/
public Thread loopDeamon() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
T t = null;
for (;;) {
try {
t = queue.take();
handler.handleMessage(t);
} catch (Exception e) {
handler.handleException(t, e);
}
}
}
});
thread.setDaemon(true);
thread.start();
return thread;
}
}
import java.util.concurrent.ArrayBlockingQueue;
/**
* 测试函数
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
Handler<String> handler = new Handler<String>() {
@Override
public void handleMessage(String msg) throws Exception {
Thread.sleep(1000);
System.out.println(msg);
}
@Override
public void handleException(String msg, Exception e) {
e.printStackTrace();
}
};
BlockingLopper<String> lopper = new BlockingLopper<String>();
lopper.setDummy(new String());
lopper.setHandler(handler);
//指定不同的阻塞队列进行测试
lopper.setQueue(new ArrayBlockingQueue<>(3));
lopper.loop();
lopper.offer("AAAA");
lopper.offer("BBBB");
lopper.offer("CCCC");
lopper.offer("DDDD");
System.out.println("--------------------");
lopper.stopLoop();
}
}
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY