2011-10-09 15 views
8

Od Javadoc z ArrayBlockingQueueArrayBlockingQueue:ArrayBlockingQueue i dodać vs put vs pojemności

dodać

public boolean add (E e)

Inserts the specified element at the tail of this queue if it is possible 
to do so immediately without exceeding the queue's capacity, returning true 
upon success and throwing an IllegalStateException if this queue is full. 

zawsze interpretowana to oświadczenie (the część if it is possible to do so immediattely) w następujący sposób:

Jeśli kolejka ma wolną pojemność, to wstawianie zakończy się pomyślnie. Jeśli nie ma pustej przestrzeni, to się nie uda.

Ale moje zrozumienie było tutaj złe.

W prostym przypadku, w którym zdecydowałem się użyć ArrayBlockingQueue dla np. 20 elementy (mała kolejka) i ma jedną nić idzie:

queue.take()

inny wątek nie dodać element do kolejki metodą add pomimo kolejki prawie pusty.

Sprawdziłem to również poprzez debugowanie.

Po zastąpieniu połączenia z queue.add(element) na queue.put(element) element rzeczywiście został dodany do kolejki.

Czym w takim razie różni się od metod?

Z jakiego innego powodu (oprócz zdolności produkcyjnej) dodatek może się nie wydarzyć?


UPDATE:

public class ConnectionListener implements Observer { 

    public static BlockingQueue<ConnectionObject> queueConnections = new ArrayBlockingQueue<ConnectionObject>(10); 

    @Override 
    public void update(Observable arg0, Object arg1) { 
     ConnectionObject con = ((ConnectionObject)arg1); 
     queueConnections.add(con); 
    } 

} 

ConnectionObject jest tylko uchwyt do wartości String.

public class ConnectionObject { 
    private String user; 
    private String ip; 
    //etc 
} 

i konsumenta:

public class ConnectionTreeUpdater extends Thread { 
    @Override 
    public void run() { 
    while(true){ 
    try { 
    final ConnectionObject con = ConnectionListener.queueConnections.take(); 

Jeśli używam add nie jest wyjątek, lecz elementem nie zostanie dodany do kolejki.

Pomyślnie: być może dlatego, że konsument czeka "w kolejce", jeśli do wewnętrznego porządkowania elementu nie można dodać, nie zostanie dodany i nie zostanie zgłoszony wyjątek. W takim przypadku.

W przeciwnym razie nie rozumiem, dlaczego nie ma wyjątków i kod put działa.

Czy put i add mają być używane inaczej?

+0

podejrzewam byłeś połowu i ignorowanie wyjątek wyrzucane przez 'add()' ale nie widząc twojego kodu, to tylko odgadnięcie. Musisz opublikować mały przykład kodu, demonstrujący występujący problem. –

+0

W rzeczywistości nie było żadnego wyjątku. Właśnie zrobiłem 'queue.add' i kod został natychmiast zwrócony bez dodawania elementu i bez wyjątku – Cratylus

+0

@ user384706: Czy możemy zobaczyć kompletny i powtarzalny przypadek testowy, który demonstruje to zachowanie (' add() 'nie rzuca wyjątku, ale także nie dodawanie elementu do kolejki). – NPE

Odpowiedz

14

To naprawdę bardzo proste:

  • czy kolejka nie jest pełna, obie metody osiągnięcia sukcesu;
  • jeśli kolejka jest pełna, add() kończy się niepowodzeniem z wyjątkiem podczas blokowania put().

Myślę, że dokumentacja jest dość wyraźna na powyższym. Jeśli nie zgadzasz się i chciałbyś drugą opinię, można zbadać kod źródłowy ArrayBlockingQueue:

public boolean add(E e) { 
    if (offer(e)) 
     return true; 
    else 
     throw new IllegalStateException("Queue full"); 
} 

public boolean offer(E e) { 
    if (e == null) throw new NullPointerException(); 
    final ReentrantLock lock = this.lock; 
    lock.lock(); 
    try { 
     if (count == items.length) 
      return false; 
     else { 
      insert(e); 
      return true; 
     } 
    } finally { 
     lock.unlock(); 
    } 
} 

public void put(E e) throws InterruptedException { 
    if (e == null) throw new NullPointerException(); 
    final E[] items = this.items; 
    final ReentrantLock lock = this.lock; 
    lock.lockInterruptibly(); 
    try { 
     try { 
      while (count == items.length) 
       notFull.await(); 
     } catch (InterruptedException ie) { 
      notFull.signal(); // propagate to non-interrupted thread 
      throw ie; 
     } 
     insert(e); 
    } finally { 
     lock.unlock(); 
    } 
} 
+0

'Jeśli znajdziesz dokumentację niejednoznaczną, możesz to sprawdzić, patrząc na kod źródłowy "- Cóż, jest różnica między szczegółami implementacji i dokumentacją, więc nie jestem z nich zadowolony. Jeśli dokumentacja jest niejasna (choć myślę, że jest to całkiem jasne w tym przypadku), otwarcie błędu jest bardziej sensowne, myślę (nie zrobiłem tego dla Javy, ale więcej niż raz dla msdn [no cóż, ap32 do win32 jest bałaganem ;-)]) – Voo

+0

@Voo: Rozumiem twój punkt widzenia. Jednak w przypadku znaczącego raportu o błędzie musi istnieć błąd (lub uzasadnione przekonanie, że istnieje błąd). W tym przypadku zarówno dokumentacja, jak i kod bardzo wyraźnie wskazują na to samo (w moich oczach). – NPE

+0

Zgadzam się, że w tym przypadku dokumentacja jest wystarczająco jasna (dobrze, a kod oczywiście robi właściwą rzecz). Tylko jeśli dokumentacja była naprawdę niejasna, samo patrzenie na implementację i zakładanie, że nigdy się nie zmieni, byłoby złym pomysłem. Stworzenie błędu w dokumentacji, aby zobaczyć, czy był to tylko uczciwy nadzór, czy pozostawiony niezdefiniowany z ważnego powodu, to imo lepszy sposób działania (oczywiście pierwsze podejście do googlowania;)) – Voo

2

Jedną z ważniejszych części debugowania problem jest pisze sprawdzian aby upewnić to, co myślisz, dzieje się naprawdę. To albo potwierdza, albo obala twoją teorię.

Sprawa badanie pokazuje, że metody używasz zachowują się dokładnie tak jak w dokumentacji (która na cytowanie) stwierdza:

public static void main(String[] args) { 

    final ArrayBlockingQueue<Integer> myQueue = 
      new ArrayBlockingQueue<Integer>(10); 


    Thread t1 = new Thread(new Runnable() { 

     public void run() 
     { 
      int i = 0; 
      while (true) 
      { 
       try 
       { 
        myQueue.add(i); 
        System.out.println("Added to queue! value: " + 
             i + 
             " size: " + myQueue.size()); 
        i++; 
       } 
       catch (Exception e) 
       { 
        System.out.println("add() threw exception, size: " + 
             myQueue.size()); 
        try 
        { 
         Thread.sleep(1000); 
        } 
        catch (InterruptedException ex) 
        { 
         Logger.getLogger(Main.class.getName()).log(Level.SEVERE, 
                    null, ex); 
        } 
       } 
      } 
     } 

    }); 

    Thread t2 = new Thread(new Runnable() { 

     public void run() 
     { 
      while (true) 
      { 
       try 
       { 
        Integer i = myQueue.take(); 
        System.out.println("Took a off the queue! value: " + 
             i + 
             " size: " + myQueue.size()); 
        Thread.sleep(100); 
       } 
       catch (InterruptedException ex) 
       { 
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, 
                   null, ex); 
       } 
      } 
     } 
    }); 

    t1.start(); 
    t2.start(); 

}