如何创建保留FIFO行为的Java PriorityBlockingQueue?

时间:2011-07-08 05:03:47

标签: java priority-queue fifo

我试图在Java中创建优先级阻塞队列,以维护具有相同优先级的元素的FIFO顺序。 Oracle文档提供了一些帮助,但我仍然非常纠结。

我应该注意以下主题对我来说都是新鲜事:泛型接口类型静态嵌套类。所有这些都在以下类定义中发挥作用。尤其是仿制药令人困惑,我确信我在这里完全搞砸了它们。

我已添加注释以识别我目前正在获得的编译器错误。

几个具体问题:

  1. 是否可以让类表示排队的事件对象,实际队列是静态类成员?

  2. 包含Oracle的FIFO事件"包装器"是否合理?作为静态嵌套类?

  3. 我是否至少在正确的轨道上,在一个外部课堂上完成所有工作?

  4. 以下是我写过的课程:

    import java.util.concurrent.PriorityBlockingQueue;
    import java.util.concurrent.atomic.AtomicLong;
    
    public class FIFOPBQEvent {
    
    /**
     * First we define a static nested class which, when instantiated,
     * encapsulates the "guts" of the event - a FIFOPBQEvent - along with
     * a sequence number that assures FIFO behavior of events of like priority.
     *  
     * The following is lifted ALMOST verbatim (I added "static" and some 
     * comments) from Oracle documentation on adding FIFO-ness to a Priority
     * Blocking Queue: 
     * http://download.oracle.com/javase/6/docs/api/java/util/concurrent/PriorityBlockingQueue.html
     * As the Oracle doc points out:
     * 
     * "A static nested class interacts with the instance members of its outer 
     * class (and other classes) just like any other top-level class. In 
     * effect, a static nested class is behaviorally a top-level class that 
     * has been nested in another top-level class for packaging convenience."
     *
     */
    static class FIFOEntry<E extends Comparable<? super E>> implements
            Comparable<FIFOEntry<E>> {
        final static AtomicLong seq = new AtomicLong();
        final long seqNum;  // instance
        final E entry;
    
        public FIFOEntry(E entry) {
            seqNum = seq.getAndIncrement();
            this.entry = entry;
        }
    
        public E getEntry() {
            return entry;
        }
        /** Here is implementation of Comparable */
        public int compareTo(FIFOEntry<E> other) {
            int res = entry.compareTo(other.entry);
            if (res == 0 && other.entry != this.entry)
                res = (seqNum < other.seqNum ? -1 : 1);
            return res;
        }
    }
    
    /**
     * Now we declare a single (static) PBQ of FIFO entries into which 
     * PBQFIFOEvents will be added and removed.
     */
    
    /** FLAGGED AS ERROR BY COMPILER */
    // Bound mismatch: The type FIFOPBQEvent is not a valid substitute for the
    // bounded parameter <E extends Comparable<? super E>> of the type 
    // FIFOPBQEvent.FIFOEntry<E>
    
    private static PriorityBlockingQueue<FIFOEntry<FIFOPBQEvent>> theQueue =
        PriorityBlockingQueue<FIFOEntry<FIFOPBQEvent>>();
    
    /** 
     * And here are the "guts" of our event: the i.d. and state of the GUI widget 
     */
    private ConsoleObject obj = ConsoleObject.UNDEFINED_OBJ; // widget that was affected
    private ObjectState state = ObjectState.UNDEFINED_STATE; // the widget's new state
    
    /** 
     * Constructor specifying the class variables 
     */
    public FIFOPBQEvent(ConsoleObject theObj, ObjectState theState) {
        obj = theObj;
        state = theState;
    }
    
    /**
     * Event queuing ("sending") and dequeuing ("receiving")
     */
    public void sendEvent() {
    
        /** FLAGGED AS ERROR BY COMPILER */
        // The method put(FIFOPBQEvent.FIFOEntry<FIFOPBQEvent>) in the type 
        // PriorityBlockingQueue<FIFOPBQEvent.FIFOEntry<FIFOPBQEvent>> is not 
        // applicable for the arguments (FIFOPBQEvent)
    
        theQueue.put(this);
    }
    
    public static FIFOPBQEvent receiveEvent() {
    
        /** FLAGGED AS ERROR BY COMPILER */
        // Type mismatch: cannot convert from FIFOPBQEvent.FIFOEntry<FIFOPBQEvent> 
        // to FIFOPBQEvent
    
        FIFOPBQEvent event = theQueue.take();
        return event;
    }
    
    /**
     * ConsoleEvent accessors
     */
    public ConsoleObject getObj() {
        return this.obj;
    }
    
    public ObjectState getState() {
        return this.state;
    }
    
    /**
     * And for the first time, enums instead of public static final ints.
     */
    public enum ConsoleObject {
        UNDEFINED_OBJ,
        RESERVED,
    
        /** Console keys */
        RESET,
        DISPLAY_MAR,
        SAVE,
        INSERT,
        RELEASE,
        START,
        SIE,
        SCE,
    
        /** Console toggle switches */
        POWER,
        PARITY_CHECK,
        IO_CHECK,
        OVERFLOW_CHECK,
        SENSE_SWITCH_1,
        SENSE_SWITCH_2,
        SENSE_SWITCH_3,
        SENSE_SWITCH_4
    }
    
    public enum ObjectState {
        UNDEFINED_STATE,
    
        /** Toggle switches */
        OFF,
        ON,
    
        /** Console keys */
        PRESSED,
    }
    }
    

4 个答案:

答案 0 :(得分:4)

第一个错误是更重要的错误。之所以发生这种情况是因为FIFOPBQEvent类没有实现Comparable,必须将其视为FIFOEntry嵌套类的泛型类型。这是因为您限制E并将其extends Comparable<...>说出来。基本上,您的FIFOPBQEvent类必须具有可比性,以提供队列的优先级(可能基于事件类型)。

要修复错误,您需要:

  1. 将班级标题更改为:

    public class FIFOPBQEvent implements Comparable<FIFOPBQEvent> {
    
  2. compareTo课程中添加FIFOPBQEvent方法;类似的东西:

    public int compareTo (FIFOPBQEvent other) {
        // TODO - compare this and other for priority
        return 0;
    }
    
  3. 然后您需要在sendEvent方法中包含您的条目:

    public void sendEvent () {
        theQueue.put(new FIFOEntry<FIFOPBQEvent> (this));
    }
    

    最后一个小错误只是您没有展开FIFOEntry对象。要解决此问题,请将receiveEvent更改为:

    public static FIFOPBQEvent receiveEvent () {
        FIFOPBQEvent event = theQueue.take ().getEntry ();
        return event;
    }
    

答案 1 :(得分:2)

让我们逐步完成您的代码。

static class FIFOEntry<E extends Comparable<? super E>> implements 
  Comparable<FIFOEntry<E>> {

这定义了带有泛型参数的类FIFOEntry。您已将泛型参数的类型约束为“任何实现自身可比较的对象”。

private static PriorityBlockingQueue<FIFOEntry<FIFOPBQEvent>> theQueue =
  PriorityBlockingQueue<FIFOEntry<FIFOPBQEvent>>();

此处您的PriorityBlockingQueue声明不正确,但您对FIFOEntry<FIFOPBQEvent>的定义不正确。这是因为上述观点 - 您已将FIFOEntry的类型限制为实现自身可比较的任何内容,即它应该是

class FIFOPBQEvent implements Comparable<FIFOPBQEvent> {

你的下一个问题是 -

public void sendEvent() {
  theQueue.put(this);
}

this的类型为FIFOPBQEvent,但队列只接受FIFOEntry个对象。要匹配您的队列签名,它应该是:

public void sendEvent() {
  // Requires FIFOPBQEvent to be Comparable
  theQueue.put(new FIFOEntry<FIFOPBQEvent>(this));     
}

您在receiveEvent()上也有同样的问题 - 您的队列签名表明Queue包含FIFOEntry对象,而您正在尝试提取FIFOPBQEvents。

答案 2 :(得分:1)

根据@ 101100的建议,我重新设计了设计,将队列与事件分离。这似乎使它更容易理解(和重用),但遗憾的是我仍然不清楚某些概念。接下来是PriorityFIFOEventQueue(为简洁起见,我省略了Event类)。而且我已经注意到我还需要一些帮助:

package ibm1620;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

/**
 * This class represents a Priority Blocking Queue of FIFO entries.  Instantiating
 * it creates a queue.  It provides instance methods for sending and receiving
 * entries, a.k.a. events of type E, on the queue.
 */

以下标记为诊断:“类型PriorityFIFOEventQueue必须实现继承的抽象方法Comparable&gt; .compareTo(PriorityFIFOEventQueue)”

我很确定我不想比较队列 !!仍然不确定我需要什么。

public class PriorityFIFOEventQueue<E extends Comparable<? super E>> implements Comparable<PriorityFIFOEventQueue<E>> {

/**
 * FIFOEntry is a static nested class that wraps an Entry and provides support for
 * keeping the Entries in FIFO sequence.
 */
private static class FIFOEntry<E> implements Comparable<FIFOEntry<E>> {

    /**
     * There's going to be one atomic seq per ... queue?  runtime?
     */

    final static AtomicLong seq = new AtomicLong();

    /**
     * FIFOEntry is simply an entry of type E, and a sequence number
     */
    final long seqNum; 
    final E    entry;

    /**
     * FIFOEntry() constructor
     */
    FIFOEntry(E entry) {
        seqNum = seq.getAndIncrement();
        this.entry = entry;
    }

    /**
     * Accessor method for entry
     */
    E getEntry() {
        return entry;
    }

    /**
     * Here is implementation of Comparable that is called directly by PBQ.
     * We first invoke E's comparator which compares based on priority. 
     * If equal priority, order by sequence number (i.e. maintain FIFO).
     * */

在下面,标记包含“compareTo”的行,诊断为 “对于类型E,方法compareTo(E)未定义”。显然我没有告诉编译器 “其他”FIFOEntry实现了Comparable。

    public int compareTo(FIFOEntry<E> other) {
        int res = entry.compareTo(other.entry); // priority-based compare
        if (res == 0 && other.entry != this.entry)
            res = (seqNum < other.seqNum ? -1 : 1); // FIFO compare
        return res;
    }
}

/**
 * Here is the PriorityBlockingQueue of FIFOEntries
 */

private PriorityBlockingQueue<FIFOEntry<E>> theQueue = 
    new PriorityBlockingQueue<FIFOEntry<E>>();

/**
 * Event queuing ("sending") and dequeuing ("receiving")
 */
public void sendEvent(E event) {
    theQueue.put(new FIFOEntry<E>(event));
}

/**
 * pollEvent() 
 * Will remove and return a ConsoleEvent from the queue, or return
 * null if queue is empty.
 */
public E pollEvent() {
    E event = null;
    FIFOEntry<E> aFIFOEvent = theQueue.poll();
    if (aFIFOEvent != null) {
        event = aFIFOEvent.getEntry();
        say("pollEvent() -> "+event);
    }
    else {
    }
    return event;
}

/**
 * receiveEvent() 
 * Will block if queue is empty.  Takes a FIFOEvent off the queue,
 * unwraps it and returns the 'E' payload.
 * 
 * @return
 */
public E receiveEvent() {
    E event = null;
    try {
        FIFOEntry<E> aFIFOEvent = theQueue.take();
        if (aFIFOEvent != null) {
            event = aFIFOEvent.getEntry();
            say("receiveEvent() -> "+event);
        }

    } catch (InterruptedException e) {
        say("InterruptedException in receiveEvent()");
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return event;
}

// for console debugging
static void say(String s) {
    System.out.println("ConsoleEvent: " + s);
}

}

答案 3 :(得分:1)

这是PriorityBlockingQueue的实际替代品,它维护具有相同优先级的项目的FIFO排序。它为用户透明地完成所有包装/解包。

此代码是为1.4 JVM编写的,并使用j.u.c.反向移植。在较新的JVM中使用它并添加泛型应该很简单。

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;

import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue;
import edu.emory.mathcs.backport.java.util.concurrent.PriorityBlockingQueue;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong;

/**
 * A queue with all properties of a {@link PriorityBlockingQueue} but which additionally 
 * returns items with equal priority 
 * in a FIFO order.
 * In this respect, {@link PriorityBlockingQueue} explicitly gives no return order guarantees for equal priority elements.
 * 
 * This queue only accepts {@link Comparable} items. A custom {@link Comparator} cannot
 * be specified at construction time.
 * 
 *
 */
public final class FIFOPriorityBlockingQueue implements BlockingQueue {
    private final PriorityBlockingQueue q;

    public FIFOPriorityBlockingQueue() {
        q = new PriorityBlockingQueue();
    }

    public FIFOPriorityBlockingQueue(int initialCapacity) {
        q = new PriorityBlockingQueue(initialCapacity);
    }

    public boolean isEmpty() {
        return q.isEmpty();
    }

    public boolean addAll(Collection c) {
    if (c == null)
        throw new NullPointerException();
    if (c == this)
        throw new IllegalArgumentException();
    boolean modified = false;
    Iterator e = c.iterator();
    while (e.hasNext()) {
        if (add(e.next()))
        modified = true;
    }
    return modified;
    }

    /**
     * Always returns <code>null</code> as this {@link BlockingQueue} only accepts
     * {@link Comparable} objects and doesn't allow setting an own {@link Comparator} 
     * @return
     */
    public Comparator comparator() {
        return null;
    }

    public boolean containsAll(Collection c) {
        Iterator e = c.iterator();
        while (e.hasNext())
            if(!contains(e.next()))
            return false;

        return true;
    }

    public int size() {
        return q.size();
    }

    public int remainingCapacity() {
        return q.remainingCapacity();
    }

    public boolean removeAll(Collection c) {
        boolean modified = false;
        Iterator e = iterator();
        while (e.hasNext()) {
            if(c.contains(e.next())) {
            e.remove();
            modified = true;
            }
        }
        return modified;
    }

    public boolean retainAll(Collection c) {
        boolean modified = false;
        Iterator e = iterator();
        while (e.hasNext()) {
            if(!c.contains(e.next())) {
            e.remove();
            modified = true;
            }
        }
        return modified;
    }

    public Object remove() {
        return ((FIFOEntry)q.remove()).entry;
    }

    public Object element() {
        return ((FIFOEntry)q.element()).entry;
    }

    public boolean add(Object e) {
        return q.add(new FIFOEntry((Comparable)e));
    }

    public boolean offer(Object e) {
        return q.offer(new FIFOEntry((Comparable)e));
    }

    public void put(Object e) {
        q.put(new FIFOEntry((Comparable)e));
    }

    public boolean offer(Object e, long timeout, TimeUnit unit) {
        return q.offer(new FIFOEntry((Comparable)e), timeout, unit);
    }

    public Object poll() {
        return ((FIFOEntry)q.poll()).entry;
    }

    public Object take() throws InterruptedException {
        return ((FIFOEntry)q.take()).entry;
    }

    public Object poll(long timeout, TimeUnit unit) throws InterruptedException {
        return ((FIFOEntry)q.poll(timeout, unit)).entry;
    }

    public Object peek() {
        return ((FIFOEntry)q.peek()).entry;
    }

    /**
     * If more than one equal objects are held by the queue, remove() will
     * remove any one of them, not necessarily the first or last inserted.
     */
    public boolean remove(Object o) {
        return q.remove(new FIFOEntry((Comparable)o));
    }

    public boolean contains(Object o) {
        return q.contains(new FIFOEntry((Comparable)o));
    }

    public Object[] toArray() {
        Object[] a = q.toArray();
        for (int i = 0; i < a.length; i++) { // unpacking
            a[i] = ((FIFOEntry)a[i]).entry;
        }
        return a;
    }

    public String toString() {
        return q.toString(); // ok, because each FIFOEntry.toString returns the toString of the entry 
    }

    public int drainTo(Collection c) {
        ArrayList tmp = new ArrayList(size());
        int n = q.drainTo(tmp);
        for (Iterator it = tmp.iterator(); it.hasNext();) {
            FIFOEntry en = (FIFOEntry) it.next();
            c.add(en.entry);
        }
        return n;
    }



    public int drainTo(Collection c, int maxElements) {
        ArrayList tmp = new ArrayList(size());
        int n = q.drainTo(tmp, maxElements);
        for (Iterator it = tmp.iterator(); it.hasNext();) {
            FIFOEntry en = (FIFOEntry) it.next();
            c.add(en.entry);
        }
        return n;
    }

    public void clear() {
        q.clear();
    }



    public Object[] toArray(Object[] a) {
        Object[] res = q.toArray(a);
        for (int i = 0; i < res.length; i++) { // unpacking
            res[i] = ((FIFOEntry)res[i]).entry;
        }
        return res;
    }



    public Iterator iterator() {
        final Iterator it = q.iterator();
        return new Iterator() {
            public void remove() throws UnsupportedOperationException, IllegalStateException, ConcurrentModificationException {
                it.remove();
            }

            public Object next() throws NoSuchElementException, ConcurrentModificationException {
                return ((FIFOEntry)it.next()).entry;
            }

            public boolean hasNext() {
                return it.hasNext();
            }
        };
    }

    public int hashCode() {
        return q.hashCode();
    }

    public boolean equals(Object obj) {
        return q.equals(obj);
    }


    /**
     * Wrapping class which adds creation ordering to a {@link Comparable} object.
     * Based on the code in the javadoc for {@link PriorityBlockingQueue}
     * 
     *
     */
    private static class FIFOEntry implements Comparable {
        private final static AtomicLong seq = new AtomicLong();
        private final long seqNum;
        private final Comparable entry;
        public FIFOEntry(Comparable entry) {
            seqNum = seq.getAndIncrement();
            this.entry = entry;
        }

        public int compareTo(Object other) {
            FIFOEntry o = (FIFOEntry) other;
            int res = entry.compareTo(o.entry);
            if (res == 0 && o.entry != this.entry)
                res = (seqNum < o.seqNum ? -1 : 1);
            return res;
        }
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((entry == null) ? 0 : entry.hashCode());
            return result;
        }
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            FIFOEntry other = (FIFOEntry) obj;
            if (entry == null) {
                if (other.entry != null)
                    return false;
            } else if (!entry.equals(other.entry))
                return false;
            return true;
        }

        public String toString() {
            return entry.toString();
        }
    }

}