2011-10-09 4 views
8

De Javadoc de ArrayBlockingQueueArrayBlockingQueue:ArrayBlockingQueue y añadir vs puesto vs capacidad

añadir

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. 

siempre interpretted esta declaración (la parte if it is possible to do so immediattely) de la siguiente manera:

Si la cola tiene capacidad libre, la inserción tendrá éxito. Si no hay espacio vacío, no tendrá éxito.

Pero entendí mal aquí.

En un caso simple que decidí usar un ArrayBlockingQueue por ej. 20 elementos (pequeña cola) y que tiene el hacer un hilo:

queue.take()

el otro hilo no añadir un elemento a la cola a través del método add a pesar de la cola estaba casi vacío.

Lo he verificado también a través de la depuración.

Una vez que reemplacé la llamada de queue.add(element) a queue.put(element), el elemento realmente se agregó a la cola.

Entonces, ¿qué es tan diferente en estos métodos?

¿Por qué otra razón (además de la capacidad) podría la adición no suceder?


ACTUALIZACIÓN:

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 es sólo un soporte para valores de cadena.

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

Y el consumidor:

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

Si uso add no es una excepción es lanzada, pero no consigue elemento añadido a la cola.

Solo un pensamiento: tal vez porque el consumidor está "esperando" en la cola, si no se puede agregar el elemento no se agregará y no se lanzará ninguna excepción. Podría ser el caso.

De lo contrario, no puedo entender por qué no hay excepción y con put el código funciona.

Son put y add destinados a ser utilizados de manera diferente?

+0

sospecho que capturaban e ignorando la excepción de ser lanzado por 'add()' pero sin ver tu código solo es una suposición. Debe publicar un pequeño ejemplo de código que demuestre el problema que está teniendo. –

+0

En realidad no hubo excepción en add. Acabo de hacer 'queue.add' y el código retornó inmediatamente sin agregar el elemento y sin excepción – Cratylus

+0

@ user384706: Por favor, podemos ver un caso de prueba completo y reproducible que demuestre este comportamiento (' add() 'no arroje una excepción sino también no agregando el elemento a la cola). – NPE

Respuesta

14

Es bastante simple en realidad:

  • si la cola no está llena, ambos métodos tienen éxito;
  • si la cola está llena, add() falla con una excepción mientras que put() bloques.

Creo que la documentación es bastante clara en lo anterior. Si no está de acuerdo, y me gustaría una segunda opinión, se podría examinar el código fuente para 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

'Si encuentra la documentación ambigua, puede verificar esto mirando el código fuente' - Bueno, hay una diferencia entre un detalle de implementación y la documentación, por lo que no estoy muy contento con ese consejo en general. Si la documentación no está clara (aunque creo que es bastante clara en este caso), creo que es más razonable abrir un error (no lo he hecho para Java, pero más de una vez para msdn [bueno, el doc api win32 es un desastre) ;-)]) – Voo

+0

@Voo: Veo tu punto. Sin embargo, para un informe de error significativo, tiene que haber un error (o una creencia razonable de que hay un error). En este caso, tanto la documentación como el código indican muy claramente lo mismo (a mis ojos de todos modos). – NPE

+0

Acepto que en este caso la documentación es lo suficientemente clara (bueno, y el código obviamente hace lo correctoTM). Solo si la documentación no estaba clara, solo mirar la implementación y asumir que nunca podría cambiar sería una mala idea. Crear un error para la documentación para ver si eso fue solo un descuido honesto o no fue definido por una buena razón es también el mejor curso de acción (bueno, primero buscando en Google;)) – Voo

2

Una de las partes más importantes de la depuración de un problema es escribiendo un caso de prueba para asegurarse de lo que crees que está sucediendo de hecho está sucediendo. Esto prueba o refuta tu teoría.

El caso de prueba a continuación muestra que los métodos que está utilizando se comportan exactamente como la documentación (que se cita) afirma:

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

} 
Cuestiones relacionadas