阻塞队列之ArrayBlockingQueue

本文深入介绍了ArrayBlockingQueue的实现原理及使用方法。ArrayBlockingQueue是一个线程安全且有界的阻塞队列,采用数组实现,并遵循FIFO原则。文章详细解释了其核心方法如offer、enqueue、put、poll、removeAt和take的具体实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayBlockingQueue是用数组实现的线程安全的有界的阻塞队列。

线程安全是指,ArrayBlockingQueue内部通过“互斥锁”保护竞争资源,实现了多线程对竞争资源的互斥访问。而有界,则是指ArrayBlockingQueue对应的数组是有界限的。 阻塞队列,是指多线程访问竞争资源时,当竞争资源已被某线程获取时,其它要获取该资源的线程需要阻塞等待;而且,ArrayBlockingQueue是按 FIFO(先进先出)原则对元素进行排序,元素都是从尾部插入到队列,从头部开始返回。

ArrayBlockingQueue实现了BlockingQueue接口,继承了AbstractQueue,内部使用Object数组存储元素,容量capacity在构造器中给出,之后不会自动扩容.其中包含ReentrantLock和两个Condition变量,用来实现互斥访问的.ReentrantLock默认是非公平锁,详情看另一篇,构造器中可以指定锁是公平锁还是非公平锁.

使用两个变量putIndex,takeIndex表示入队和出队的位置.

具体实现:

1, offer;

public boolean offer(E var1) {
    checkNotNull(var1);   //判null,拋NullPointerException
    ReentrantLock var2 = this.lock;
    var2.lock();

    boolean var3;
    try {
        if(this.count == this.items.length) {   //条件count==items.length时,队列已满,返回false;
            var3 = false;
            return var3;
        }

        this.enqueue(var1);  //入队
        var3 = true;
    } finally {
        var2.unlock();
    }

    return var3;
}
主要是使用ReentrantLock的lock()方法,其他的逻辑就是队列满了返回false,不满入队,返回true.

2,enqueue

private void enqueue(E var1) {
    Object[] var2 = this.items;
    var2[this.putIndex] = var1;
    if(++this.putIndex == var2.length) { //因为队列由数组实现,当队列末尾没空间时,从数组开端入队,循环数组
        this.putIndex = 0;
    }

    ++this.count;
    this.notEmpty.signal();
}

3,put

public void put(E var1) throws InterruptedException {
    checkNotNull(var1);
    ReentrantLock var2 = this.lock;
    var2.lockInterruptibly();  // lockInterruptibly()等待锁或保持锁的过程中被中断,抛出InterruptedException

    try {
        while(this.count == this.items.length) {
            this.notFull.await();     //这里是和offer不一样的地方,一直阻塞到有空间加入,当收到notFull.signal()时,便可以入队
        }

        this.enqueue(var1);
    } finally {
        var2.unlock();
    }

}

4,poll

public E poll(long var1, TimeUnit var3) throws InterruptedException {  //没有参数的poll方法,除了不等待,其他逻辑一样
    long var4 = var3.toNanos(var1);
    ReentrantLock var6 = this.lock;
    var6.lockInterruptibly();

    try {
        Object var7;
        while(this.count == 0) {
            if(var4 <= 0L) {
                var7 = null;
                return var7;
            }

            var4 = this.notEmpty.awaitNanos(var4); //等待var1时间
        }

        var7 = this.dequeue();
        return var7;
    } finally {
        var6.unlock();
    }
}
5,removeAt

void removeAt(int var1) {
    Object[] var2 = this.items;
    if(var1 == this.takeIndex) { //如果正好等于出队下标,正常出队
        var2[this.takeIndex] = null;
        if(++this.takeIndex == var2.length) {
            this.takeIndex = 0;
        }

        --this.count;
        if(this.itrs != null) {
            this.itrs.elementDequeued();
        }
    } else {                                      //将要删除的元素后面的元素往前移
        int var3 = this.putIndex;
        int var4 = var1;

        while(true) {
            int var5 = var4 + 1;
            if(var5 == var2.length) {
                var5 = 0;
            }

            if(var5 == var3) {
                var2[var4] = null;
                this.putIndex = var4;
                --this.count;
                if(this.itrs != null) {
                    this.itrs.removedAt(var1);
                }
                break;
            }

            var2[var4] = var2[var5];            
            var4 = var5;
        }
    }

    this.notFull.signal();
}
6,take

public E take() throws InterruptedException {
    ReentrantLock var1 = this.lock;
    var1.lockInterruptibly();

    Object var2;
    try {
        while(this.count == 0) {
            this.notEmpty.await(); //一直阻塞
        }

        var2 = this.dequeue();
    } finally {
        var1.unlock();
    }

    return var2;
}
offer和poll对应,put和take对应

ArrayBlockingQueue使用示例:

1,生产者Producer:

public class Producer implements Runnable{

    //容器
    private final ArrayBlockingQueue<Bread> queue;

    Producer(ArrayBlockingQueue<Bread> queue){
        this.queue = queue;
    }
    
    @Override
    public void run() {
        while(true){
            produce();
        }
    }

    private void produce(){
        /**
         * put()方法是如果容器满了的话就会把当前线程挂起
         * offer()方法是容器如果满的话就会返回false         */
        try {
            Bread bread = new Bread();
            queue.put(bread);
            System.out.println("Producer:"+bread);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
2,消费者Consumer:

public class Consumer implements Runnable{
    //容器
    private final ArrayBlockingQueue<Bread> queue;

    Consumer(ArrayBlockingQueue<Bread> queue){
        this.queue = queue;
    }
    
    @Override
    public void run() {
        while (true) {
            consume();
        }
    }
    
    private void consume(){
        /**
         * take()方法和put()方法是对应的,从中拿一个数据,如果拿不到线程挂起
         * poll()方法和offer()方法是对应的,从中拿一个数据,如果没有直接返回null
         */
        try {
            Bread bread = queue.take();
            System.out.println("consumer:"+bread);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
3,生产的面包Bread:

public class Bread {
    private int size;

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    @Override
    public String toString() {
        return "Bread{" +
                "size=" + size +
                '}';
    }
}

4,测试Test:

public class Test {
    public static void main(String[] args) {
        int capacity = 10;
        ArrayBlockingQueue<Bread> queue = new ArrayBlockingQueue<>(capacity);

        new Thread(new Producer(queue)).start();
        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
        new Thread(new Consumer(queue)).start();
        new Thread(new Consumer(queue)).start();
    }
}

具体逻辑其实就是BlockingQueue那一篇写的,只是用数组实现了队列,最重要的是去理解ReentrantLock和Condition.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值