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然后进行自旋。
出队时用的就是加锁来实现的