相关文章:
JAVA多线程创建、使用看这一篇就够了Java多线程进阶实战
文章目录
- 一.ArrayBlockingQueue 介绍
- 二、创建ArrayBlockingQueue
- 2.1 默认非公平阻塞队列
- 2.2 公平阻塞队列
- 2.3 给定集合的元素,按集合迭代器的遍历顺序添加
- 三、使用ArrayBlockingQueue
- 3.1 添加
- 3.1.1 offer()方法
- 3.1.2 add()方法
- 3.1.3 put()方法
- 3.1.4 总结
- 3.2 删除
- 3.2.1 remove()
- 3.2.2 poll()
- 3.2.3 take()
- 3.3 检查方法
- 3.3.1 element()
- 3.3.2 peek()
- 扩展
- 关注 Java有货领取更多资料
一.ArrayBlockingQueue 介绍
ArrayBlockingQueue是一个阻塞式的队列,继承自AbstractBlockingQueue,实现了BlockingQueue接口。
底层以数组的形式保存数据(Object [])。常用的操作包括 add,offer,put,remove,poll,take,peek。
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {}
根据 ArrayBlockingQueue 的名字我们都可以看出,它是一个队列,并且是一个基于数组的阻塞队列。
ArrayBlockingQueue 是一个有界队列,有界也就意味着,它不能够存储无限多数量的对象。
所以在创建 ArrayBlockingQueue 时,必须要给它指定一个队列的大小。
二、创建ArrayBlockingQueue
ArrayBlockingQueue 为我们提供了三种创建方式:
2.1 默认非公平阻塞队列
示例:
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(10);
源码:
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
这里内部调用了公平锁方法,只是默认未开启
2.2 公平阻塞队列
示例:
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(10,true);
源码:
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();
}
2.3 给定集合的元素,按集合迭代器的遍历顺序添加
这个看你参数的传达是公平还是非公平,内部是for循环添加,也进行加锁处理
示例:
ArrayBlockingQueue<String> blockingQueue2 = new ArrayBlockingQueue<String>(10, true, new ArrayList<>());
源码:
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();
}
}
三、使用ArrayBlockingQueue
3.1 添加
这里添加也为我们提供了三种方式,我们一一说来;
3.1.1 offer()方法
示例 :
blockingQueue.offer("Java有货");
源码:
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
解析: 首先会对我们的参数进行非空检验,之后获取全局锁,获取之后判断是否达到最大长度,到达返回false,否则进行新增,成功返回true
3.1.2 add()方法
示例 :
blockingQueue.add("Java有货");
源码:
public boolean add(E e) {
return super.add(e);
}
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
解析: 这里也会调用offer(),不同之处在于,**add()**方法添加失败,会报错,不是很友好
3.1.3 put()方法
示例 :
try {
blockingQueue.put("Java有货");
} catch (InterruptedException e) {
e.printStackTrace();
}
源码:
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
解析:大家可以看出,调用**put()**方法他调用了 notFull.await();,也就是说,当队列,长多到达最大值时,在进行新增操作,会使对了阻塞,直到条件不满足时在新增,否则一直阻塞;
3.1.4 总结
根据以上几种方式的介绍,与使用,**add()**方法会直接报错,put()方法如果一直阻塞,对服务器的性能消耗太大,所以个人觉得使用offer() 方法更加有利于系统的性能,毕竟失败我们也知道,可以做后续的处理
3.2 删除
3.2.1 remove()
示例:
blockingQueue.remove("Java有货");
源码:
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i])) {
removeAt(i);
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
解析:首先判断我们的元素是否为null,不为null,则通过final Object[] items = this.items;获取当前对列,然后进行 do while 循环操作,进行删除,成功为true,否则为false
3.2.2 poll()
获取对列的首个元素
示例:
blockingQueue.poll("Java有货");
源码:
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}
解析:这里主要dequeue()方法,如果获取的对列无元素,直接返回null,否则就是获取对列的第一个元素,赋值给E 然后将获取后的下标元素重置为null,之后就是下标,长度的加减操作
3.2.3 take()
获取对列的首个元素
示例:
try {
blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
源码:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
解析:这里不多做解释,和之前的新增类
3.3 检查方法
3.3.1 element()
获取但不移除此队列的头元素,没有元素则抛异常
3.3.2 peek()
获取但不移除此队列的头;若队列为空,则返回 null。
这里也不多说,和之前的都差不多
扩展
下面这段代码大家应该也很熟悉,判断是否包含,内容和之前解说的差不多,也是获取元素,进行循环判定,
public boolean contains(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i]))
return true;
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
技术博客:
SpringCloud学习代码: https://github.com/Dylan-haiji/javayh-cloud
Redis、Mongo、Rabbitmq、Kafka学习代码: https://github.com/Dylan-haiji/javayh-middleware
AlibabaCloud学习代码:https://github.com/Dylan-haiji/javayh-cloud-nacos
SpringBoot+SpringSecurity实现自定义登录学习代码:https://github.com/Dylan-haiji/javayh-distribution