阻塞队列之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 小时前
小厂三年我现在怎么样了
后端·面试
无风听海4 小时前
深入理解 ASP.NET Core 中的 IActionResult
后端·asp.net
霸道流氓气质4 小时前
Spring Boot + MyBatis-Plus 实现异常隔离的 Upsert 数据落库(含远程调用数据补全)
spring boot·后端·mybatis
IT_陈寒4 小时前
React状态更新后视图不刷新?我差点以为是灵异事件
前端·人工智能·后端
不懂的浪漫4 小时前
01|从 Spring Boot 项目理解 RAG:ingest、query、rerank、trace 到 eval
java·人工智能·spring boot·后端·ai·rag
无风听海4 小时前
ASP.NET Core Results<T1, T2>深度解析
后端·asp.net
__log4 小时前
NestJS vs Spring Boot:从架构哲学到实战选择的技术全景解析
spring boot·后端·架构·typescript
拽着尾巴的鱼儿4 小时前
国密算法 Spring Boot 实战:SM2/SM3/SM4 完整集成指南
spring boot·后端·算法