阻塞队列之ArrayBlockingQueue

ArrayBlockingQueue是一个有界阻塞队列,内部用数组存储元素,初始化的需要指定容量大小,底层用ReentrantLock来保证线程安全.

例子如下:

arduino 复制代码
public class ArrayBlockingQueueDemo {

    public static void main(String[] args) throws Exception {
        //使用ArrayBlockingQueue初始化一个BlockingQueue,指定容量的上限为1024
        BlockingQueue queue = new ArrayBlockingQueue(1024);

        //生产者
        Producer producer = new Producer(queue);
        //消费者
        Consumer consumer = new Consumer(queue);

        //开启生产者线程
        new Thread(producer).start();
        //开启消费者线程
        new Thread(consumer).start();  

        Thread.sleep(4000);
    }
}

生产者如下:

arduino 复制代码
public class Producer implements Runnable {

    protected BlockingQueue queue = null;

    public Producer(BlockingQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 3; i++) {
                queue.put(i);
                System.out.println("produce " + i);
                Thread.sleep(10000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

1:从运行结果可以看出来,生产者放入一个值,就会消费一个.生产者和消费者效率速度匹配的情况下还是不错的.如果任何一方过快或者过慢,都会造成线程的阻塞.

2:从这个例子中,我们可以看到几个关键的方法.

方法探究:

1:构造方法:

csharp 复制代码
  public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }
    
      public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

​1.1:从构造方法我们可以看出,这个阻塞队列是支持公平和非公平两种模式.

1.2:如果我们传入的容量参数小于等于0会抛出异常.

1.3:内部存储元素的数据结构是一个数组.

1.4:内部运用的锁是ReentrantLcok,阻塞队列运用的是condition.

(ReentrantLcok前面有相关的文章可以参考,这里就不过多赘述了.)

2:构造方法

ini 复制代码
 public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

这个构造方法和上面的区别是这个可以传入一个数组.

2.1:通过获取锁操作,来保证多线程下的初始化唯一.

2.2:通过循环给items数组进行赋值.

2.3:checkNotNull方法:

typescript 复制代码
 private static void checkNotNull(Object v) {
        if (v == null)
            throw new NullPointerException();
    }

如果传入的数组为空的话抛出空指针异常.

2.4:count:当前队列里的元素数量.

2.5:putIndex:放入元素的指针.(就是位置,如果i等于当前容量,就重置为0,是一个环形数组.).

3.put方法:

java 复制代码
void put(E e) throws InterruptedException;

3.1:enqueue方法:

注意:这里的唤醒并不是真正的唤醒,只是把线程从条件队里转移到同步队列,然后释放锁的时候去真正唤醒.

4.take方法:

csharp 复制代码
E take() throws InterruptedException;
复制代码

4.1dequeue方法:

4.2:itrs是一个迭代器,我们可以通过迭代器遍历这个阻塞队列,一般情况下这个都是空.

4.3elementDequeued方法:

scss 复制代码
 void elementDequeued() {
            // assert lock.getHoldCount() == 1;
            if (count == 0)
                queueIsEmpty();
            else if (takeIndex == 0)
                takeIndexWrapped();
        }

4.4如果当前队列没有元素的话,进入queueIsEmpty方法:

4.5如果取指针为0的话,进入takeIndexWrapped方法:

ini 复制代码
 void takeIndexWrapped() {
            // assert lock.getHoldCount() == 1;
            cycles++;
            for (Node o = null, p = head; p != null;) {
                final Itr it = p.get();
                final Node next = p.next;
                if (it == null || it.takeIndexWrapped()) {
                    // unlink p
                    // assert it == null || it.isDetached();
                    p.clear();
                    p.next = null;
                    if (o == null)
                        head = next;
                    else
                        o.next = next;
                } else {
                    o = p;
                }
                p = next;
            }
            if (head == null)   // no more iterators to track
                itrs = null;
        }

这个方法大概就是要把已经出队的元素进行清除.

相关推荐
稀土熊猫君4 分钟前
一个人能做出什么开源项目?
vue.js·后端·开源
lizhongxuan6 分钟前
Agent Runtime 中的 Code-driven Assembly
后端
货拉拉技术15 分钟前
资损下降 99.96% 的背后: AI 资损防控平台实战
后端
用户83580861879138 分钟前
撮合引擎 OrderBook 的 100ns 之路:无锁 RingBuffer + 伪共享消除,Go 1.22 下单 op 11ns
后端
用户8818630013641 分钟前
用Node.js写一个简单的API请求日志中间件
后端
用户8358086187911 小时前
Go 高并发下的“内存刺客“:自研 Size-Class 无锁对象池,把 sync.Pool 的 P99 从 128μs 压到 41μs
后端
货拉拉技术1 小时前
资损下降 99.96% 的背后— AI 资损防控平台实战
后端
山水洛行3 小时前
AI Agent 智能体记忆:从检索到被治理的数据系统
后端
卷无止境3 小时前
C++20 的概念与约束:让模板编程终于"说人话"
后端
Ai拆代码的曹操3 小时前
一次排查三种连接泄漏模式,再也不怕 HikariCP 连接池爆满了
后端