ArrayBlockingQueue是什么
ArrayBlockingQueue是最典型的有界阻塞队列。 内部使用数组存储元素! 初始化时需要指定容量大小。 利用 ReentrantLock 实现线程安全!
ArrayBlockingQueue的适用场景
在生产者-消费者模型中使用时,如果生产速度和消费速度基本匹配的情况下,可以使用ArrayBlockingQueue。 当如果生产速度远远大于消费速度,则会导致队列填满,大量生产线程被阻塞。
ArrayBlockingQueue的实现原理
使用独占锁ReentrantLock实现线程安全,入队和出队操作使用同一个锁对象,也就是只能有一个线程可以进行入队或者出队操作; 意味着生产者和消费者无法并行操作,在高并发场景下会成为性能瓶颈。
ArrayBlockingQueue的特点
有界队列!先进先出!存取互相排斥! 使用的数据结构是静态数组:容量固定,没有扩容机制;没有元素的位置也占用空间,被 null 占位; 使用ReentrantLock锁:存取是同一把锁,操作的是同一个数组对象,存取互相排斥。
ArrayBlockingQueue的入队出队操作
两个指针都是从队首向队尾移动,保证队列的先进先出原则! 入队阻塞对象notFull:队列count=length,放不进去元素时,阻塞在该对象上。 出队阻塞对象notEmpty:队列count=0,无元素可取时,阻塞在该对象上。 入队操作:从队首开始添加元素,记录putIndex(到队尾时设置为0),唤醒notEmpty。 出队操作:从队首开始取出元素,记录takeIndex(到队尾时设置为0),唤醒notFull。
ArrayBlockingQueue的使用方式
BlockingQueue < Integer > blockingQueue = new ArrayBlockingQueue < Integer > ( 1000 ) ;
System . out. println ( blockingQueue. add ( 9 ) ) ;
blockingQueue. put ( 10 ) ;
System . out. println ( blockingQueue. take ( ) ) ;
System . out. println ( blockingQueue. take ( ) ) ;
ArrayBlockingQueue的数据结构源码分析
final Object [ ] items;
int takeIndex;
int putIndex;
int count;
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
ArrayBlockingQueue的构造方法源码分析
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 ( ) ;
}
public ArrayBlockingQueue ( int capacity, boolean fair,
Collection < ? extends E > c) {
this ( capacity, fair) ;
final ReentrantLock lock = this . lock;
lock. lock ( ) ;
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的入队方法:put(E e) 源码分析
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 ( ) ;
}
}
private void enqueue ( E x) {
final Object [ ] items = this . items;
items[ putIndex] = x;
if ( ++ putIndex == items. length)
putIndex = 0 ;
count++ ;
notEmpty. signal ( ) ;
}
public void lockInterruptibly ( ) throws InterruptedException {
sync. acquireInterruptibly ( 1 ) ;
}
public final void acquireInterruptibly ( int arg)
throws InterruptedException {
if ( Thread . interrupted ( ) )
throw new InterruptedException ( ) ;
if ( ! tryAcquire ( arg) )
doAcquireInterruptibly ( arg) ;
}
private void doAcquireInterruptibly ( int arg)
throws InterruptedException {
final Node node = addWaiter ( Node . EXCLUSIVE) ;
boolean failed = true ;
try {
for ( ; ; ) {
final Node p = node. predecessor ( ) ;
if ( p == head && tryAcquire ( arg) ) {
setHead ( node) ;
p. next = null ;
failed = false ;
return ;
}
if ( shouldParkAfterFailedAcquire ( p, node) &&
parkAndCheckInterrupt ( ) )
throw new InterruptedException ( ) ;
}
} finally {
if ( failed)
cancelAcquire ( node) ;
}
}
public final void signal ( ) {
if ( ! isHeldExclusively ( ) )
throw new IllegalMonitorStateException ( ) ;
Node first = firstWaiter;
if ( first != null )
doSignal ( first) ;
}
private void doSignal ( Node first) {
do {
if ( ( firstWaiter = first. nextWaiter) == null )
lastWaiter = null ;
first. nextWaiter = null ;
} while ( ! transferForSignal ( first) &&
( first = firstWaiter) != null ) ;
}
ArrayBlockingQueue的出队方法:take() 源码分析
public E take ( ) throws InterruptedException {
final ReentrantLock lock = this . lock;
lock. lockInterruptibly ( ) ;
try {
while ( count == 0 )
notEmpty. await ( ) ;
return dequeue ( ) ;
} finally {
lock. unlock ( ) ;
}
}
private E dequeue ( ) {
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;
}
结束语
获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师! 关注公众号,可以让你对MySQL有非常深入的了解 关注公众号,每天持续高效的了解并发编程! 关注公众号,后续持续高效的了解spring源码! 这个公众号,无广告!!!每日更新!!!