2017-12-18 1 views
0

ArrayBlockingQueueのような単純なキューを作成しようとしていますが、要素を追加している間にキューがいっぱいになると、クラスは、単にキューの先頭から要素を取得するには、キュー ArrayBlockingQueue要素が追加されているときにキューがいっぱいになった場合にキューヘッドが削除される

  • のサイズを取得するには、パブリックメソッド

    • 下回っている必要があります。使用可能な要素がない場合。
    • は、キューの末尾に要素を追加するには

    誰かが以下のコードを確認し、これを行うには良い方法があるなら、私が知っていることはできますか?

    public class CircularArrayNonBlockingQueue<E> { 
        private ArrayBlockingQueue<E> blockingQueue; 
    
        public CircularArrayNonBlockingQueue(int size) { 
         blockingQueue = new ArrayBlockingQueue<>(size); 
        } 
    
        public synchronized int size() { 
         return blockingQueue.size(); 
        } 
    
        public synchronized void add(E element) { 
         if(blockingQueue.remainingCapacity() <= 0) { 
          blockingQueue.poll(); 
         } 
         blockingQueue.add(element); 
        } 
    
        public synchronized E poll() { 
         return blockingQueue.poll(); 
        } 
    } 
    

    私はすべてのメソッド​​を行う必要はありませんコメントでの議論をもとにEDIT 。更新されたコードは以下のようになります。

    public class CircularNonBlockingQueue<E> { 
        private final ArrayBlockingQueue<E> blockingQueue; 
    
        public CircularNonBlockingQueue(int size) { 
         blockingQueue = new ArrayBlockingQueue<>(size); 
        } 
    
        public int size() { 
         return blockingQueue.size(); 
        } 
    
        public synchronized void add(E element) { 
         if(blockingQueue.remainingCapacity() <= 0) { 
          blockingQueue.poll(); 
         } 
         blockingQueue.add(element); 
        } 
    
        public E take() throws InterruptedException { 
         return blockingQueue.take(); 
        } 
    } 
    
  • +0

    私によく見える:-) – PillHead

    +0

    ReadWriteLockを使って同期を最適化することができるかもしれません – PillHead

    +0

    @PillHead - もう少し説明してください。 – tuk

    答えて

    1

    スレッドセーフなバックエンドコレクションを使用しても、必ずしも正しいプログラムが作成されるとは限りません。あなただけのadd方法が​​あるとき、add内でごif(blockingQueue.remainingCapacity() <= 0)テストした後、同時に実行take()は、要素を削除し、そのaddpoll()が不必要に要素を削除する可能性があるので、take()方法は、それに並行して実行することができます。消費スレッドが異なるアイテムを受け取るので、add()take()の前に完了する状況には、知覚可能な違いがあります。つまり、addが最も古いアイテムを削除しない場合がありますが、2番目に古いアイテムが削除されることがあります。あなたがに関して弱いの保証と一緒に暮らすことができる場合は、しかし、

    import java.util.ArrayDeque; 
    
    public class CircularNonBlockingQueue<E> { 
        private final ArrayDeque<E> blockingQueue; 
        private final int maxSize; 
    
        public CircularNonBlockingQueue(int size) { 
         if(size<1) throw new IllegalArgumentException("size == "+size); 
         blockingQueue = new ArrayDeque<>(size); 
         maxSize = size; 
        } 
    
        public synchronized int size() { 
         return blockingQueue.size(); 
        } 
    
        public synchronized void add(E element) { 
         if(blockingQueue.size() == maxSize) { 
          blockingQueue.poll(); 
         } 
         blockingQueue.add(element); 
         notify(); 
        } 
    
        public synchronized E take() throws InterruptedException { 
         while(blockingQueue.isEmpty()) wait(); 
         return blockingQueue.remove(); 
        } 
    } 
    

    一方

    あなたが一貫してあなたの方法のすべてに​​を使用する場合は、スレッドセーフなバックエンドのコレクションを持っている必要はありません最古要素、あなたはBlockingQueueを使用することができますし、任意の​​必要はありません。これらのソリューションのどちらも「公平さ」を提供することに留意しなければならない

    public class CircularNonBlockingQueue<E> { 
        private final ArrayBlockingQueue<E> blockingQueue; 
    
        public CircularNonBlockingQueue(int size) { 
         blockingQueue = new ArrayBlockingQueue<>(size); 
        } 
    
        public int size() { 
         return blockingQueue.size(); 
        } 
    
        public void add(E element) { 
         while(!blockingQueue.offer(element)) { 
          blockingQueue.poll(); 
         } 
        } 
    
        public E take() throws InterruptedException { 
         return blockingQueue.take(); 
        } 
    } 
    

    を。したがって、プロデューサおよびコンシューマスレッドの数がキューの容量に比べて大きい場合、生産者はtake()でブロックされたスレッドを再アクティブ化せずに繰り返しアイテムを削除するリスクがあります。したがって、常に十分な容量を確保する必要があります。

    関連する問題