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(); 
        } 
    } 
    

    나는 모든 방법 synchronized을 할 필요가 없습니다 코멘트에 대한 논의를 바탕으로 편집 . 업데이트 된 코드는 다음과 같습니다.

    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 방법은 take() 방법은 그것을 동시에 실행할 수 있습니다 synchronized, 그래서 add 내에 if(blockingQueue.remainingCapacity() <= 0) 시험 후에하는 동시에 실행 take()이 요소를 제거, 그래서 addpoll() 불필요 요소를 제거 할 가능성이있는 경우. 소비 스레드가 다른 항목을 수신하므로 add()take() 전에 완료되는 상황에는 인식 가능한 차이가 있습니다. 다시 말해, add은 가장 오래된 항목을 제거하지 않지만 두 번째로 오래된 항목은 제거하는 것처럼 보입니다. 당신에 대한 약한 보증을 살 수 있다면, 그러나

    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(); 
        } 
    } 
    

    :

    한편

    당신이 일관 방법의 모든 synchronized를 사용하는 경우, 스레드 안전 백엔드 컬렉션이 할 필요가 없다 가장 오래된 요소는, 당신은 BlockingQueue 사용할 수 있습니다 및 synchronized 필요하지 않은 :

    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()에 차단 된 스레드를 다시 활성화하지 않고 항목을 반복적으로 제거 할 위험이 있습니다. 따라서 항상 충분한 용량을 확보해야합니다.

    관련 문제