线程安全CircularFIFOBlockingQueue

时间:2018-07-10 12:30:12

标签: java multithreading queue locking

我必须使用ThreadSafe LinkedBlockingQueue,如果队列已满,它将从头部丢弃元素。

Apache提供了CircularFifoQueue,但这似乎不是线程安全的。

现在为了实现这一点,我正在更新从多个线程调用的类的write方法。

 void addElement(Element e) throws InterruptedException {
        if(queue.size()==MAX_SIZE)
        {
            queue.remove();
        }
        queue.put(e);
    }

现在在内部检查尺寸,删除和放置使用锁的所有这三个操作。但是用我的方法,他们没有持有任何单一的锁。

现在,我的理解是,即使它们使用内部锁,但仍然在语句末尾释放这些锁。这3个操作不是原子的。

因此,有1个线程检查大小并继续删除的机会,但是由于现在有1个可用空间,其他线程最终插入了该元素。

请确认我的理解是否正确。还有没有在我的writeElement方法中放置通用锁的更好的方法来实现这一点。我不想放任何可以避免的锁,因为这种写方法是从多个线程调用的。

更新:另外,如果我像下面这样使用它,这是readWrite锁的正确用法。在这种情况下,我的目的是仅在我从队列中删除元素时才将写入操作与锁同步。

static final ReadWriteLock readWriteLockOnQueue = new ReentrantReadWriteLock();

 void addElement(Element e) throws InterruptedException {
        if(queue.size()==MAX_SIZE)
        {
            readWriteLockOnQueue.writeLock().lock();
            queue.remove();
            readWriteLockOnQueue.writeLock().unlock();
        }
        readWriteLockOnQueue.readLock().lock();
        queue.put(e);
        readWriteLockOnQueue.readLock().unlock();
    }

1 个答案:

答案 0 :(得分:0)

如果您确实证明性能问题是由阻止和上下文切换引起的,那么您可以自己实现CircularQueue的非阻止版本。

您可以看一下Apache的实现,为了实现非阻塞线程安全行为,您需要进行的更改是必须将队列状态保留在单个原始变量(int中, long,或您认为方便的方式),然后将其CAS。因此,您的非阻塞队列如下所示:

public class CircularQueue<E> {
    private final short maxSize;
    private final AtomicLong ctl;
    private final E[] elements;

    public CircularQueue(short maxSize){
        if(maxSize <= 0) throw new IllegalArgumentException();
        this.maxSize = maxSize;
        ctl = new AtomicLong(); //initial state, end = 0, start = 0, full = false, update in progress = false
        elements = (E[]) new Object[maxSize];
    }

    public boolean addElement(E e){
        final AtomicLong queueState = this.ctl;

        boolean stateUpdated = false;
        long insertIdx = 0;
        long ctl = 0;
        while(!stateUpdated) {
            long currentState = queueState.get();
            if ((currentState & FULL_BIT) != 0) {
                return false;
            } else {
                insertIdx = (short) ((currentState & END_MASK) >>> END_SHIFT);
                long newInsertIdx = insertIdx + 1;
                if(newInsertIdx == maxSize)
                    newInsertIdx = 0;
                if(newInsertIdx == (currentState & START_MASK))
                    ctl = ctlOfNewEndAndFull(currentState, newInsertIdx, true);
                else
                    ctl = ctlOfNewEndAndFull(currentState, newInsertIdx, false);

                stateUpdated = queueState.compareAndSet(currentState, ctl);
            }
        }

        elements[(int) insertIdx] = e;

        long mru = ((ctl & MOST_RECENT_UPD_ELEMENT_INDEX_MASK) >>> MOST_RECENT_UPD_ELEMENT_INDEX_SHIFT);

        while(((mru + 1) % maxSize) != insertIdx && mru != insertIdx){
            ctl = queueState.get();
            mru = (ctl & MOST_RECENT_UPD_ELEMENT_INDEX_MASK) >>> MOST_RECENT_UPD_ELEMENT_INDEX_SHIFT;
        }

        while(!queueState.compareAndSet(ctl, setMru(ctl, insertIdx))){
            ctl = queueState.get();
        }

        return true;
    }

    public E peek(){
        final AtomicLong queueState = this.ctl;

        long ctl = queueState.get();
        long start = ctl & START_MASK;
        long end = (ctl & END_MASK) >>> END_SHIFT;
        boolean full = (ctl & FULL_BIT) != 0;

        while(true) {
            if (start == end & !full)
                return null;
            else if (queueState.compareAndSet(ctl, peekElemStateUpdate(ctl, (start + 1) % maxSize))){
                return elements[(int) start];
            } else {
                ctl = queueState.get();
            }
        }
    }

    public static final long END_MASK = 0xFFFF00000000L; //short
    public static final long END_SHIFT = 32;

    public static final long MOST_RECENT_UPD_ELEMENT_INDEX_MASK = 0xFFFF0000L; //short
    public static final long MOST_RECENT_UPD_ELEMENT_INDEX_SHIFT = 16;

    public static final long START_MASK = 0xFFFFL; //short

    public static final long FULL_BIT = 1L << 63;

    private static long peekElemStateUpdate(long oldState, long start){
        return oldState & ~(START_MASK | FULL_BIT) | start;
    }

    private static long setMru(long oldState, long mru){
        return (oldState & ~MOST_RECENT_UPD_ELEMENT_INDEX_MASK) | (mru << MOST_RECENT_UPD_ELEMENT_INDEX_SHIFT);
    }

    private static long ctlOfNewEndAndFull(long oldState, long newEnd, boolean full){
        if(!full)
            return (oldState & ~(END_MASK | FULL_BIT)) | (newEnd << END_SHIFT);
        else
            return (oldState & ~(END_MASK | FULL_BIT)) | (newEnd << END_SHIFT | FULL_BIT);
    }
}

我没有衡量在激烈争用下的实际性能,因此您可能需要针对特定​​用例进行调整。我也建议您检查它是否包含种族(据我所知不包含种族)。