Java并发学习笔记(七)-ArrayBlockingQueue

Java并发包concurrent里提供了3个阻塞队列,ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue。阻塞队列通常用于生产消费模式,满队列时生产者阻塞,空队列时消费者阻塞。ArrayBlockingQueue是一个有界的阻塞队列,此队列按FIFO原则增加删除元素。底层实现是一个数组。

public class ArrayBlockingQueueDemo {

public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
BlockingQueue<Bread> queue = new ArrayBlockingQueue<Bread>(10);
for (int i = 0; i < 2; i++) {
es.execute(new Baker(queue));
}

for (int i = 0; i < 10; i++) {
es.execute(new BreadConsumer(queue));
}
es.shutdown();
}

}

class Baker implements Runnable {
private static int no = 0;
private int id = ++no;
private int count = 0;

private BlockingQueue<Bread> queue;

public Baker(BlockingQueue<Bread> queue) {
this.queue = queue;
}

@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("面包师" + id + "正准备做第" + ++count + "面包");
Bread bread = new Bread();
// 满队列情况下,阻塞
try {
queue.put(bread);
System.out.println("面包师" + id + "做的第" + count + "面包是面包"
+ bread.getId());
} catch (InterruptedException e) {
}
}
}
}

class BreadConsumer implements Runnable {
private static int no = 0;
private int id = ++no;
private int count = 0;

private BlockingQueue<Bread> queue;

public BreadConsumer(BlockingQueue<Bread> queue) {
this.queue = queue;
}

@Override
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println("顾客 " + id + "准备买第" + ++count + "个面包");

// 空队列情况下,阻塞
try {
Bread bread = queue.take();
System.out.println("顾客" + id + "买到的第" + count + "个面包是面包"
+ bread.getId());
} catch (InterruptedException e) {
}
}
}

}

class Bread {
private static int count = 0;
private int id = ++count;

public int getId() {
return id;
}
}

该类也提供了其它一些非阻塞的方法,如add,offer,pull等。看看它的实现。

构造函数

    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();
    }

capacity是初始化时数组长度,fair是表示是否为公平锁(这里不介绍这个)。我们可以看到,在构造函数里,有一个Object数组,保存队列元素,有两个lock的condition,分别表示非空队列条件及非满队列条件。

再看阻塞插入方法put

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            insert(e);
        } finally {
            lock.unlock();
        }
    }


正式插入元素之前,使用可中断的lock锁。count表示当前队列里的元素个数。如果队列元素个数等于数组长度,那么非满队列条件阻塞,等待队列非满时唤醒。如果队列不满,则插入。

    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();
    }

insert方法,在队列尾部插入一个元素,重新对putIndex计算,队列元素个数加1,并在最后唤醒非空队列条件,表示可以从队列中取出元素。

再来看阻塞方法take

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return extract();
        } finally {
            lock.unlock();
        }
    }

与put类似,获取锁,再判断队列是否为空,空队列,则非空队列条件等待,刚才的insert方法在插入一个元素后,会唤醒这个条件。如果队列不为空,则取出一个元素。

    private E extract() {
        final Object[] items = this.items;
        E x = this.<E>cast(items[takeIndex]);
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal();
        return x;
    }

extract方法从队列头部读取一个元素,并置数组中该元素为null,防止内存泄露,队列元素个数减1,并唤醒非满队列条件,说明已经不是满队列,可以插入元素了。


offer和pull的实现也比较相似,只是没有阻塞这个动作

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : extract();
        } finally {
            lock.unlock();
        }
    }

我们在insert和extract里看到inc(int)方法。

    final int inc(int i) {
        return (++i == items.length) ? 0 : i;
    }

如果当前索引等于数组长度,则置为0,不然不变,可以看出来,这里的数组作为队列的容器,是循环使用的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃鸳鸯锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值