【阻塞队列详解】

一、阻塞队列

1.1、队列

  • 是一种先进先出的线性表,限定一段进行元素插入,另一端进行元素的弹出与删除

在这里插入图片描述

1.2、阻塞队列

阻塞队列相比队列下,区别在于,当阻塞队列插入数据时,如果队列满了,那线程将会阻塞,等待队列为非满时,进行插入,在阻塞队列中获取数据时,如果队列中没数据,当前线程就会阻塞,直到队列中有数据后,才能获取数据
在这里插入图片描述

1.3、应用场景

  1. 线程池:线程池中的任务队列通常是一个阻塞队列。当任务数超过线程池的容量时,新提交的任务将被放入任务队列中等待执行。线程池中的工作线程从任务队列中取出任务进行处理,如果队列为空,则工作线程会被阻塞,直到队列中有新的任务被提交
  2. 生产者-消费者模型:在生产者-消费者模型中,生产者向队列中添加元素,消费者从队列中取出元素进行处理。阻塞队列可以很好地解决生产者和消费者之间的并发问题,避免线程间的竞争和冲突。
  3. 消息队列:消息队列使用阻塞队列来存储消息,生产者将消息放入队列中,消费者从队列中取出消息进行处理。消息队列可以实现异步通信,提高系统的吞吐量和响应性能,同时还可以将不同的组件解耦,提高系统的可维护性和可扩展性。
  4. 缓存系统:缓存系统使用阻塞队列来存储缓存数据,当缓存数据被更新时,它会被放入队列中,其他线程可以从队列中取出最新的数据进行使用。使用阻塞队列可以避免并发更新缓存数据时的竞争和冲突。
  5. 并发任务处理:在并发任务处理中,可以将待处理的任务放入阻塞队列中,多个工作线程可以从队列中取出任务进行处理。使用阻塞队列可以避免多个线程同时处理同一个任务的问题,并且可以将任务的提交和执行解耦,提高系统的可维护性和可扩展性。

二、JUC并发包下的阻塞队列

2.1、ArrayBlockingQueue

2.1.1、ArrayBlockingQueue定义

ArrayBlockingQueue是最典型的有界阻塞队列,其内部是用数组存储元素的,初始化时需要指定容量大小,利用 ReentrantLock 实现线程安全。ArrayBlockingQueue可以用于实现数据缓存、限流、生产者-消费者模式等各种应用

2.1.2、ArrayBlockingQueue的使用

BlockingQueue queue = new ArrayBlockingQueue(1024);//声明ArrayBlockingQueue队列
queue.put("1"); //向队列中添加元素
Object object = queue.take(); //从队列中取出元素

2.1.3、ArrayBlockingQueue原理

ArrayBlockingQueue使用独占锁ReentrantLock实现线程安全,入队和出队操作使用同一个锁对象,也就是只能有一个线程可以进行入队或者出队操作,这也就意味着生产者和消费者无法并行操作,在高并发场景可能会成为性能瓶颈

2.2、LinkedBlockingQueue

2.2.1、LinkedBlockingQueue定义

LinkedBlockingQueue是一个基于链表实现的阻塞队列,默认情况下,该阻塞队列的大小为Integer.MAX_VALUE,由于这个数值特别大,所以 LinkedBlockingQueue 也被称作无界队列,代表它几乎没有界限,队列可以随着元素的添加而动态增长,但是如果没有剩余内存,则队列将抛出OOM错误。所以为了避免队列过大造成机器负载或者内存爆满的情况出现,我们在使用的时候建议手动传一个队列的大小。

2.2.2、LinkedBlockingQueue使用

//指定队列的大小创建有界队列
 BlockingQueue<Integer> boundedQueue = new LinkedBlockingQueue<>(100);
boundedQueue .put("1"); //向队列中添加元素
Object object = boundedQueue .take(); //从队列中取出元素
//无界队列
BlockingQueue<Integer> unboundedQueue = new LinkedBlockingQueue<>();

2.2.3、LinkedBlockingQueue原理

LinkedBlockingQueue内部由单链表实现,只能从head取元素,从tail添加元素。LinkedBlockingQueue采用两把锁的锁分离技术实现入队出队互不阻塞,添加元素和获取元素都有独立的锁,也就是说LinkedBlockingQueue是读写分离的,读写操作可以并行执行

2.3、DelayQueue

2.3.1、DelayQueue定义

DelayQueue 是一个支持延时获取元素的阻塞队列, 内部采用优先队列 PriorityQueue 存储元素,同时元素必须实现 Delayed 接口;在创建元素时可以指定多久才可以从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素。延迟队列的特点是:不是先进先出,而是会按照延迟时间的长短来排序,下一个即将执行的任务会排到队列的最前面。

2.3.2、DelayQueue使用

如下为延迟订单的场景例子:

public class DelayQueueExample {

	public static void main(String[] args) throws InterruptedException {
		DelayQueue<Order> delayQueue = new DelayQueue<>();
		
		// 添加三个订单,分别延迟 5 秒、2 秒和 3 秒
		delayQueue.put(new Order("order1", System.currentTimeMillis(), 5000));
		delayQueue.put(new Order("order2", System.currentTimeMillis(), 2000));
		delayQueue.put(new Order("order3", System.currentTimeMillis(), 3000));
		
		 // 循环取出订单,直到所有订单都被处理完毕
		 while (!delayQueue.isEmpty()) {
		 Order order = delayQueue.take();
		 System.out.println("处理订单:" + order.getOrderId());
		 }
	 }
	
	 static class Order implements Delayed{
		 private String orderId;
		 private long createTime;
		 private long delayTime;
		
		 public Order(String orderId, long createTime, long delayTime) {
		 this.orderId = orderId;
		 this.createTime = createTime;
		 this.delayTime = delayTime;
	 	}
	
	 	public String getOrderId() {
		 return orderId;
	 	}
	
		 @Override
		 public long getDelay(TimeUnit unit) {
			 long diff = createTime + delayTime - System.currentTimeMillis();
			 return unit.convert(diff, TimeUnit.MILLISECONDS);
		 }
	
		 @Override
		 public int compareTo(Delayed o) {
			 long diff = this.getDelay(TimeUnit.MILLISECONDS) -.getDelay(TimeUnit.MILLISECONDS);
			 return Long.compare(diff, 0);
	 	}
	 }
 }

2.3.3、DelayQueue原理

内部维护了一个最大/最小堆,每次获取数据时,就会拿其最小或者最大的之,进行加入时间参数等待与唤醒,利用 ReentrantLock 实现线程安全

//用于保证队列操作的线程安全
private final transient ReentrantLock lock = new ReentrantLock();
// 优先级队列,存储元素,用于保证延迟低的优先执行
private final PriorityQueue<E> q = new PriorityQueue<E>();
// 用于标记当前是否有线程在排队(仅用于取元素时) leader 指向的是第一个从队列获取元素阻塞的线程
private Thread leader = null;
// 条件,用于表示现在是否有可取的元素 当新元素到达,或新线程可能需要成为leader时被通知
private final Condition available = lock.newCondition();
public DelayQueue() {}
public DelayQueue(Collection<? extends E> c) {
	this.addAll(c);
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值