博客
关于我
技术专家面试实战: 用数组实现一个阻塞队列?
阅读量:587 次
发布时间:2019-03-09

本文共 2999 字,大约阅读时间需要 9 分钟。

ArrayBlockingQueue是一种基于数组实现的阻塞队列,广泛应用于生产者消费者模式中的任务缓冲,如线程池中的工作队列。那么,如何用数组实现这种阻塞队列呢?让我们一步步深入探讨。

ArrayBlockingQueue的核心功能

ArrayBlockingQueue作为一个队列,需要具备入队和出队的基本操作。由于它是阻塞队列,当队列已满时,入队操作应阻塞,直到队列有空位可用;而当队列为空时,出队操作也应阻塞,直到队列中有元素可供获取。同时,由于ArrayBlockingQueue通常在多线程环境中使用,线程安全性是其核心需求。

实现ArrayBlockingQueue的步骤

我们可以将问题分解为两个主要部分:用数组实现队列以及实现条件阻塞和线程安全。

用数组实现队列

实现队列的入队和出队操作,可以通过循环数组的方式来处理。这种方式避免了每次出队都需要移动大量元素的问题,时间复杂度为O(1)。具体来说,我们可以使用两个索引变量,分别记录下一个要入队和下一个要出队的位置。当入队到数组末尾时,从0开始重新计算;同样,当出队到末尾时,也从0开始重新计算。同时,使用一个计数器来存储当前队列的元素数量。

实现条件阻塞和线程安全

在Java中,实现条件等待有两种主要方式:synchronized + Object.wait和Lock + Condition.await。选择后者可以更好地支持线程安全,因为它不仅支持中断,还能支持多个条件。

通过Lock和Condition,我们可以定义两个条件:一个用于检测队列是否不空(notEmpty),另一个用于检测队列是否已满(notFull)。当入队时,先检查队列是否已满,如果满了则调用notFull.await()进行条件等待;入队完成后,唤醒等待不空条件的线程。同样,出队操作也是类似的逻辑。

ArrayBlockingQueue的具体实现

下面是一个实现ArrayBlockingQueue的代码示例:

class ArrayBlockingQueue
{
private final Object[] items;
private int takeIndex;
private int putIndex;
private int count;
private ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
public ArrayBlockingQueue(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException();
}
this.items = new Object[capacity];
lock = new ReentrantLock();
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
private void enqueue(E e) {
Object[] items = this.items;
if (putIndex < items.length && count < items.length) {
items[putIndex] = e;
putIndex = (putIndex + 1) % items.length;
count++;
notFull.signal();
}
}
private E dequeue() {
Object[] items = this.items;
E e = (E) items[takeIndex];
items[takeIndex] = null;
takeIndex = (takeIndex + 1) % items.length;
count--;
notEmpty.signal();
return e;
}
public void put(E e) throws InterruptedException {
lock.lockInterruptibly();
try {
while (count == items.length) {
notFull.await();
}
enqueue(e);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
lock.lockInterruptibly();
try {
while (count == 0) {
notEmpty.await();
}
E e = dequeue();
notFull.signal();
return e;
} finally {
lock.unlock();
}
}
}

代码解释

  • 初始化:构造函数初始化数组、锁和条件。检查容量是否合理,否则抛出异常。

  • 入队操作:在锁的保护下,判断队列是否已满。如果已满,调用notFull.await()进行条件等待;否则,执行入队操作,并调用notEmpty.signal()唤醒等待线程。

  • 出队操作:在锁的保护下,判断队列是否为空。如果为空,调用notEmpty.await()进行条件等待;否则,执行出队操作,并调用notFull.signal()唤醒等待线程。

  • 代码中的细节

  • put方法中的局部lock变量:这是为了优化性能,避免在方法内频繁访问实例变量时引起的性能 overhead。

  • while循环的锁内执行:确保在多个线程下正确的进入和退出队列状态,防止多个线程同时入队或出队。

  • 通过上述实现,我们可以清晰地看到ArrayBlockingQueue如何在数组中实现高效的入队和出队操作,同时确保线程安全和条件阻塞。这种设计在多线程环境中非常有用,特别是在需要生产者和消费者之间交换任务的情况下。

    转载地址:http://hmtpz.baihongyu.com/

    你可能感兴趣的文章
    Mysql 整形列的字节与存储范围
    查看>>
    mysql 断电数据损坏,无法启动
    查看>>
    MySQL 日期时间类型的选择
    查看>>
    Mysql 时间操作(当天,昨天,7天,30天,半年,全年,季度)
    查看>>
    MySQL 是如何加锁的?
    查看>>
    MySQL 是怎样运行的 - InnoDB数据页结构
    查看>>
    mysql 更新子表_mysql 在update中实现子查询的方式
    查看>>
    MySQL 有什么优点?
    查看>>
    mysql 权限整理记录
    查看>>
    mysql 权限登录问题:ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)
    查看>>
    MYSQL 查看最大连接数和修改最大连接数
    查看>>
    MySQL 查看有哪些表
    查看>>
    mysql 查看锁_阿里/美团/字节面试官必问的Mysql锁机制,你真的明白吗
    查看>>
    MySql 查询以逗号分隔的字符串的方法(正则)
    查看>>
    MySQL 查询优化:提速查询效率的13大秘籍(避免使用SELECT 、分页查询的优化、合理使用连接、子查询的优化)(上)
    查看>>
    mysql 查询数据库所有表的字段信息
    查看>>
    【Java基础】什么是面向对象?
    查看>>
    mysql 查询,正数降序排序,负数升序排序
    查看>>
    MySQL 树形结构 根据指定节点 获取其下属的所有子节点(包含路径上的枝干节点和叶子节点)...
    查看>>
    mysql 死锁 Deadlock found when trying to get lock; try restarting transaction
    查看>>