PriorityBlockingQueue是线程安全的,之前我们看过priorityqueue的实现原理,https://www.jianshu.com/p/b0a2615f1bba?v=1672747035605
今天我们看一下PriorityBlockingQueue怎么实现线程安全的
有个类的全局变量retrantlock。是在PriorityBlockingQueue的构造方法里面新建的。
offer方法里面使用了retrantlock锁处理保证线程安全
在offer元素过程中可能需要扩容
扩容代码实现
private void tryGrow(Object[] array, int oldCap) {
lock.unlock(); // must release and then re-acquire main lock。//注释一 释放全局锁
Object[] newArray =null;
if (allocationSpinLock ==0 &&UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset, 0, 1)) {//注释二
try {
int newCap = oldCap + ((oldCap <64) ?
(oldCap +2) :// grow faster if small
(oldCap >>1));
if (newCap -MAX_ARRAY_SIZE >0) {// possible overflow
int minCap = oldCap +1;
if (minCap <0 || minCap >MAX_ARRAY_SIZE)
throw new OutOfMemoryError();
newCap =MAX_ARRAY_SIZE;
}
if (newCap > oldCap &&queue == array)
newArray =new Object[newCap];
}finally {
allocationSpinLock =0;
}
}if (newArray ==null)// back off if another thread is allocating
Thread.yield();
lock.lock(); //注释三
if (newArray !=null &&queue == array) {
queue = newArray;
System.arraycopy(array, 0, newArray, 0, oldCap);
}}
入队时如何保证线程安全和效率
1.offer方法里面获取了自旋锁,保证了只有一个线程进行添加操作。
2.扩容方法(注释一 )会释放锁。这样保证offer方法可以进行入队操作
3.注释二 cas操作保证只有一个线程进行扩容
4.然后获取锁进行数据拷贝到扩容后的数组,因为这个操作会随着数据的增长耗费些时间。
所以采用了加锁操作。其他线程会进行yield然后进行自旋。
出队时用的就是加锁来实现的