在删除之前需要K访问的队列数据结构

时间:2015-03-15 12:51:56

标签: algorithm data-structures

我需要一个特殊的类似队列的数据结构。它可以由多个消费者使用,但是在消费者阅读后,队列中的每个项目都必须从队列中删除。

是否有任何生产准备实施?或者我应该在每个项目中实现一个带有读取计数器的队列,并自己处理项目删除吗?

提前致谢。

2 个答案:

答案 0 :(得分:0)

我认为这就是你要找的东西。源自BlockingQueue的源代码。警告,未经测试。

我试图找到一种方法来包装Queue,但是Queue没有公开它的并发成员,因此你无法获得正确的语义。

public class CountingQueue<E> {

    private class Entry {
        Entry(int count, E element) {
            this.count = count;
            this.element = element;
        }
        int count;
        E element;
    }

    public CountingQueue(int capacity) {
        if (capacity <= 0) {
            throw new IllegalArgumentException();
        }
        this.items = new Object[capacity];
        this.lock = new ReentrantLock(false);
        this.condition  = this.lock.newCondition();
    }

    private final ReentrantLock lock;
    private final Condition condition;
    private final Object[] items;
    private int takeIndex;
    private int putIndex;
    private int count;

    final int inc(int i) {
        return (++i == items.length) ? 0 : i;
    }

    final int dec(int i) {
        return ((i == 0) ? items.length : i) - 1;
    }

    private static void checkNotNull(Object v) {
        if (v == null)
            throw new NullPointerException();
    }

    /**
     * Inserts element at current put position, advances, and signals.
     * Call only when holding lock.
     */
    private void insert(int count, E x) {
        items[putIndex] = new Entry(count, x);
        putIndex = inc(putIndex);
        if (count++ == 0) {
            // empty to non-empty
            condition.signal();
        }
    }

    private E extract() {
        Entry entry = (Entry)items[takeIndex];
        if (--entry.count <= 0) {
            items[takeIndex] = null;
            takeIndex = inc(takeIndex);
            if (count-- == items.length) {
                // full to not-full
                condition.signal();
            }
        }
        return entry.element;
    }

    private boolean waitNotEmpty(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        while (count == 0) {
            if (nanos <= 0) {
                return false;
            }
            nanos = this.condition.awaitNanos(nanos);
        }
        return true;
    }

    private boolean waitNotFull(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        while (count == items.length) {
            if (nanos <= 0)
                return false;
            nanos = condition.awaitNanos(nanos);
        }
        return true;
    }

    public boolean put(int count, E e) {
        checkNotNull(e);
        final ReentrantLock localLock = this.lock;
        localLock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                insert(count, e);
                return true;
            }
        } finally {
            localLock.unlock();
        }
    }

    public boolean put(int count, E e, long timeout, TimeUnit unit)
        throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock localLock = this.lock;
        localLock.lockInterruptibly();
        try {
            if (!waitNotFull(timeout, unit)) {
                return false;
            }
            insert(count, e);
            return true;
        } finally {
            localLock.unlock();
        }
    }

    public E get() {
        final ReentrantLock localLock = this.lock;
        localLock.lock();
        try {
            return (count == 0) ? null : extract();
        } finally {
            localLock.unlock();
        }
    }

    public E get(long timeout, TimeUnit unit) throws InterruptedException {
        final ReentrantLock localLock = this.lock;
        localLock.lockInterruptibly();
        try {
            if (waitNotEmpty(timeout, unit)) {
                return extract();
            } else {
                return null;
            }
        } finally {
            localLock.unlock();
        }
    }

    public int size() {
        final ReentrantLock localLock = this.lock;
        localLock.lock();
        try {
            return count;
        } finally {
            localLock.unlock();
        }
    }

    public boolean isEmpty() {
        final ReentrantLock localLock = this.lock;
        localLock.lock();
        try {
            return count == 0;
        } finally {
            localLock.unlock();
        }
    }

    public int remainingCapacity() {
        final ReentrantLock lock= this.lock;
        lock.lock();
        try {
            return items.length - count;
        } finally {
            lock.unlock();
        }
    }

    public boolean isFull() {
        final ReentrantLock localLock = this.lock;
        localLock.lock();
        try {
            return items.length - count == 0;
        } finally {
            localLock.unlock();
        }
    }

    public void clear() {
        final ReentrantLock localLock = this.lock;
        localLock.lock();
        try {
            for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
                items[i] = null;
            count = 0;
            putIndex = 0;
            takeIndex = 0;
            condition.signalAll();
        } finally {
            localLock.unlock();
        }
    }

}

答案 1 :(得分:0)

保留所需信息的内存有效方式:

每个队列条目变为

Set<ConsumerID>

这样您就可以确保k个不同的消费者使用k次:您的应用逻辑会检查是否

set.size()==k

并在这种情况下将其从队列中删除。

在存储方面:您将基于

进行权衡,以实现Set实现
  • ConsumerID的大小和类型
  • 检索速度要求

例如,如果k非常小,并且您的队列检索逻辑可以访问

Map<ID,ConsumerId> 

那么你可以简单地使用Int或者甚至是Short或Byte,这取决于#excellence ConsumerID并且可能存储在数组中。这比访问集合要慢,因为它会线性遍历 - 但对于小K来说可能是合理的。