Java源碼閱讀之PriorityBlockingQueue

Summary:

  • public class PriorityBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable 
  • 本類與普通的PriorityBlockingQueue類中的方法基本一致,區別在於本類的方法都會在方法執行前先加鎖執行完再解鎖;
  • add、put會調用notEmpty.signal();喚醒等待的線程
  • take如果遇到隊列爲空則;調用await()方法,阻塞知道被喚醒

Fields:

private final ReentrantLock lock; //鎖
private final Condition notEmpty; //條件鎖,用於隊列爲空時阻塞
private transient volatile int allocationSpinLock;

private transient Comparator<? super E> comparator; //比較器
private transient Object[] queue;

Constructor:

public PriorityBlockingQueue(int initialCapacity,Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        this.comparator = comparator;
        this.queue = new Object[initialCapacity];
}

put&add

//該方法會阻塞,添加成功返回true,否則爆出異常
//添加成功會觸發notEmpty.signal();使得原本在讀取數據時阻塞的線程能夠解除阻塞
public void put(E e) {
        offer(e); 
}
public boolean add(E e) {
        return offer(e);
}
public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock(); //此處上鎖,所以offer方法會產生阻塞
        int n, cap;
        Object[] array;
        while ((n = size) >= (cap = (array = queue).length))
            tryGrow(array, cap);   //擴容,直到擴容成功才退出循環,注意這裏擴容的工作並不一定是本線程完成的,具體原因看擴容方法tryGrow
        try {
            Comparator<? super E> cmp = comparator;
            if (cmp == null)
                siftUpComparable(n, e, array); //調整堆中數據,與普通優先隊列無異
            else
                siftUpUsingComparator(n, e, array, cmp); //調整堆中數據,與普通優先隊列無異
            size = n + 1;
            notEmpty.signal(); //解除空隊列的條件阻塞
        } finally {
            lock.unlock(); //解鎖
        }
        return true;
}

take

//從隊列中讀取一個數據,如果隊列沒有數據則阻塞
public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        E result;
        try {
            while ( (result = dequeue()) == null)
                notEmpty.await();
        } finally {
            lock.unlock();
        }
        return result;
}
private E dequeue() {
        int n = size - 1;
        if (n < 0)
            return null;
        else {
            Object[] array = queue;
            E result = (E) array[0];
            E x = (E) array[n];
            array[n] = null;
            Comparator<? super E> cmp = comparator;
            if (cmp == null)
                siftDownComparable(0, x, array, n);
            else
                siftDownUsingComparator(0, x, array, n, cmp);
            size = n;
            return result;
        }
}

poll

//該方法只是嘗試去從隊列中獲取第一個數,如果沒有則返回null
public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return dequeue();
        } finally {
            lock.unlock();
        }
}

擴容操作;

該操作先釋放鎖;最後再嘗試獲取鎖
然後很多線可能都來請求擴容,先進行CAS操作判斷當前是否只有自己一個線程在擴容;
如果是則正常情況擴容;
不是則直到只有自己一個線程再去正常擴容;
正常庫容還會判斷自己是否是第一次在這裏擴容,如果不是則不操作System.arraycopy()
private void tryGrow(Object[] array, int oldCap) {
        lock.unlock(); //先釋放鎖
        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);
        }
}

UNSAFE

上面出現的UNSAFE是一種非阻塞同步機制;
大概意思就是先操作,如果達不到預期目的再操作一次,直到成功爲止;
CAS操作;只有通過bootstrap ClassLoader加載的class才能訪問它;
    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long allocationSpinLockOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = PriorityBlockingQueue.class;
            allocationSpinLockOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("allocationSpinLock"));
        } catch (Exception e) {
            throw new Error(e);
        }
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章