Java 阻塞队列
- 入队阻塞:当队列已满时,尝试入队的线程会被阻塞,直到队列有空闲空间。
- 出队阻塞:当队列为空时,尝试出队的线程会被阻塞,直到队列中有元素。
| 操作类型 | 抛出异常 | 返回特殊值 | 阻塞等待 | 超时等待 |
|---|---|---|---|---|
| 入队 | add(e) | offer(e) | put(e) | offer(e, timeout, unit) |
| 出队 | remove() | poll() | take() | poll(timeout, unit) |
| 查看队首 | element() | peek() | 无 | 无 |
| 实现类 | 特点 | 适用场景 |
|---|---|---|
| ArrayBlockingQueue | 基于数组、有界、公平 / 非公平锁 | 固定容量的生产消费 |
| LinkedBlockingQueue | 基于链表、默认无界(Integer.MAX_VALUE) | 任务队列(如线程池) |
| SynchronousQueue | 无存储、入队必须等出队(一对一传递) | 快速传递数据(如 Executors.newCachedThreadPool ()) |
| DelayQueue | 延迟队列、元素需实现 Delayed 接口 | 定时任务(如定时关闭连接) |
阻塞队列用法
java
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueDemo {
// 定义有界阻塞队列,容量为3
private static final BlockingQueue<String> QUEUE = new ArrayBlockingQueue<>(3);
public static void main(String[] args) {
// 1. 生产者线程:不断往队列放数据
new Thread(() -> {
try {
int count = 1;
while (true) {
String data = "武汉美食" + count++;
QUEUE.put(data); // 队列满时会阻塞
System.out.println("生产者放入:" + data + " | 队列当前大小:" + QUEUE.size());
Thread.sleep(500); // 模拟生产耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
System.out.println("生产者被中断");
}
}, "生产者").start();
// 2. 消费者线程:不断从队列取数据
new Thread(() -> {
try {
while (true) {
String data = QUEUE.take(); // 队列空时会阻塞
System.out.println("消费者取出:" + data + " | 队列当前大小:" + QUEUE.size());
Thread.sleep(1000); // 模拟消费耗时(比生产慢,队列会慢慢满)
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("消费者被中断");
}
}, "消费者").start();
}
}
场景1 异步消息处理(生产者 - 消费者进阶)
比如电商系统中,用户下单后,主线程只负责保存订单,下单成功的通知、库存扣减等耗时操作,交给异步线程处理 ------ 用阻塞队列解耦主线程和异步线程。
java
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.BlockingQueue;
// 订单实体
class Order {
private String orderId;
private String goodsName;
public Order(String orderId, String goodsName) {
this.orderId = orderId;
this.goodsName = goodsName;
}
// getter
public String getOrderId() { return orderId; }
public String getGoodsName() { return goodsName; }
}
public class OrderMessageDemo {
// 订单消息队列(指定容量100,避免无限堆积)
private static final BlockingQueue<Order> ORDER_QUEUE = new LinkedBlockingQueue<>(100);
// 初始化消费者线程(项目启动时启动)
static {
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
// 阻塞等待新订单
Order order = ORDER_QUEUE.take();
// 处理异步逻辑:扣库存、发通知
handleOrderAsync(order);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("订单处理线程被中断");
break;
}
}
}, "订单处理线程").start();
}
// 主线程:下单接口
public static void createOrder(String orderId, String goodsName) {
try {
// 1. 保存订单(核心逻辑,快速完成)
System.out.println("主线程:保存订单 " + orderId + " 成功");
// 2. 放入阻塞队列,异步处理后续逻辑
ORDER_QUEUE.put(new Order(orderId, goodsName));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("下单失败:队列阻塞被中断");
}
}
// 异步处理订单的逻辑
private static void handleOrderAsync(Order order) {
System.out.println("异步线程:处理订单 " + order.getOrderId()
+ ",扣减[" + order.getGoodsName() + "]库存,发送下单成功通知");
// 模拟耗时操作
try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
// 测试
public static void main(String[] args) {
// 模拟用户下单(主线程快速响应)
createOrder("O20260223001", "武汉热干面");
createOrder("O20260223002", "周黑鸭");
createOrder("O20260223003", "武昌鱼");
// 等待异步线程处理完成
try { Thread.sleep(2000); } catch (InterruptedException e) {}
}
}
java
主线程:保存订单 O20260223001 成功
主线程:保存订单 O20260223002 成功
主线程:保存订单 O20260223003 成功
异步线程:处理订单 O20260223001,扣减[武汉热干面]库存,发送下单成功通知
异步线程:处理订单 O20260223002,扣减[周黑鸭]库存,发送下单成功通知
异步线程:处理订单 O20260223003,扣减[武昌鱼]库存,发送下单成功通知
场景2 延迟队列(DelayQueue)------ 定时任务
比如:用户下单后 30 分钟未付款,自动取消订单;连接空闲 5 分钟后自动关闭。DelayQueue 可以实现这种 "延迟执行" 的需求,元素必须实现Delayed接口。
java
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
// 延迟订单任务(实现Delayed接口)
class DelayOrderTask implements Delayed {
private String orderId;
// 任务执行的截止时间(毫秒)
private long expireTime;
public DelayOrderTask(String orderId, long delayTime, TimeUnit unit) {
this.orderId = orderId;
// 计算截止时间:当前时间 + 延迟时间
this.expireTime = System.currentTimeMillis() + unit.toMillis(delayTime);
}
// 核心方法:返回剩余延迟时间(<=0时,队列会取出该元素)
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
// 排序用(DelayQueue是优先级队列)
@Override
public int compareTo(Delayed o) {
return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
}
// 取消订单的逻辑
public void cancelOrder() {
System.out.println("定时任务:订单" + orderId + "超时未付款,自动取消");
}
}
public class DelayQueueDemo {
public static void main(String[] args) throws InterruptedException {
DelayQueue<DelayOrderTask> delayQueue = new DelayQueue<>();
// 1. 添加延迟任务:3秒后取消订单O001,1秒后取消订单O002
delayQueue.put(new DelayOrderTask("O001", 3, TimeUnit.SECONDS));
delayQueue.put(new DelayOrderTask("O002", 1, TimeUnit.SECONDS));
System.out.println("添加延迟任务完成,等待执行...");
// 2. 消费延迟队列(线程中循环执行)
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
// 阻塞等待:直到有任务到期
DelayOrderTask task = delayQueue.take();
task.cancelOrder(); // 执行取消订单逻辑
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
// 等待任务执行完成
Thread.sleep(4000);
}
}
java
添加延迟任务完成,等待执行...
定时任务:订单O002超时未付款,自动取消
定时任务:订单O001超时未付款,自动取消