2010-04-26 12 views

Respuesta

42

No hay un comportamiento garantizado para lo que sucede cuando se llama simultáneamente a add por dos hilos en ArrayList. Sin embargo, según mi experiencia, ambos objetos se han agregado bien. La mayoría de los problemas de seguridad de subprocesos relacionados con las listas se relacionan con la iteración al agregar/eliminar. A pesar de esto, recomiendo encarecidamente no utilizar ArrayList de vanilla con múltiples hilos y acceso concurrente.

El vector solía ser el estándar para las listas concurrentes, pero ahora el estándar es usar el Collections synchronized list.

También recomiendo Java Concurrency in Practice de Goetz et al si va a pasar algún tiempo trabajando con hilos en Java. El libro cubre este tema con mucho mejor detalle.

+13

"Ha sido mi experiencia que ambos objetos se han agregado bien" solo quiero señalar que esto es pura suerte. Es probable que la ventana para un problema de corrupción de datos con ArrayList sea extremadamente pequeña, pero aún existe –

+5

Correcto, y ese es mi punto. En la gran mayoría de los casos, no habrá problema; pero no programamos para la mayoría de los casos. Por lo tanto, recomiendo encarecidamente no usar ArrayList. – derivation

2

puede usar List l = Collections.synchronizedList(new ArrayList()); si quiere la versión segura de subprocesos de arrayList.

0

http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html

Tenga en cuenta que esta aplicación no está sincronizado. Si varios subprocesos acceden a una instancia de ArrayList al mismo tiempo, y al menos uno de los subprocesos modifica estructuralmente la lista, debe estar sincronizada externamente.

Como no hay sincronización interna, lo que usted teoriza no es plausible.

Por lo tanto, las cosas no se sincronizan, con resultados desagradables e impredecibles.

+0

Derecha, vi eso. Pensé que se habría lanzado una 'ConcurrentModificationException' que no nos está sucediendo. Tal vez nos encontremos con otro efecto secundario de no usar una colección segura para hilos. –

+0

@Marcus que parece ser solo el caso si modificar la lista mientras se itera sobre ella. Además, no hay garantía de que se lanzará esta excepción. – WhirlWind

1

java.util.concurrent tiene una lista de matrices segura para subprocesos. El ArrayList estándar no es seguro para subprocesos y el comportamiento cuando varios subprocesos se actualizan al mismo tiempo no está definido. También puede haber comportamientos extraños con múltiples lectores cuando uno o más hilos están escribiendo al mismo tiempo.

3

También puede obtener un null, un ArrayOutOfBoundsException, o algo que queda hasta la implementación. Se ha observado que HashMap s entran en un ciclo infinito en los sistemas de producción. Realmente no necesita saber qué podría salir mal, simplemente no lo haga.

Puede usar Vector, pero tiende a funcionar la interfaz no es lo suficientemente rica. Probablemente encontrará que desea una estructura de datos diferente en la mayoría de los casos.

3

El comportamiento probablemente no está definido, ya que ArrayList no es seguro para la fabricación de hilos. Si modifica la lista mientras un Iterator está intentado sobre ella, obtendrá una ConcurrentModificationException. Puede envolver ArrayList con Collection.synchronizedList o usar una colección de hilos seguros (hay muchos), o simplemente agregar las llamadas de agregar en un bloque sincronizado.

14

Puede suceder cualquier cantidad de cosas. Puede obtener ambos objetos añadidos correctamente. Puede obtener solo uno de los objetos agregados. Podría obtener una excepción ArrayIndexOutOfBounds porque el tamaño de la matriz subyacente no se ajustó correctamente. O pueden suceder otras cosas. Baste decir que no puede confiar en que ocurra ningún comportamiento.

Como alternativa, puede usar Vector, puede usar Collections.synchronizedList, puede usar CopyOnWriteArrayList, o puede usar un candado separado. Todo depende de qué más esté haciendo y qué tipo de control tiene sobre el acceso a la colección.

3

Se me ocurrió el siguiente código para imitar un escenario del mundo real.

100 tareas se ejecutan en paralelo y actualizan su estado completo al programa principal. Uso CountDownLatch para esperar la finalización de la tarea.

import java.util.concurrent.*; 
import java.util.*; 

public class Runner { 

    // Should be replaced with Collections.synchronizedList(new ArrayList<Integer>()) 
    public List<Integer> completed = new ArrayList<Integer>(); 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     Runner r = new Runner(); 
     ExecutorService exe = Executors.newFixedThreadPool(30); 
     int tasks = 100; 
     CountDownLatch latch = new CountDownLatch(tasks); 
     for (int i = 0; i < tasks; i++) { 
      exe.submit(r.new Task(i, latch)); 
     } 
     try { 
      latch.await(); 
      System.out.println("Summary:"); 
      System.out.println("Number of tasks completed: " 
        + r.completed.size()); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     exe.shutdown(); 
    } 

    class Task implements Runnable { 

     private int id; 
     private CountDownLatch latch; 

     public Task(int id, CountDownLatch latch) { 
      this.id = id; 
      this.latch = latch; 
     } 

     public void run() { 
      Random r = new Random(); 
      try { 
       Thread.sleep(r.nextInt(5000)); //Actual work of the task 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      completed.add(id); 
      latch.countDown(); 
     } 
    } 
} 

cuando me encontré con la aplicación de 10 veces y al menos 3 a 4 veces el programa no se imprime un número correcto de las tareas completadas. Lo ideal sería imprimir 100 (si no se producen excepciones). Pero en algunos casos estaba imprimiendo 98, 99, etc.

Por lo tanto, prueba que las actualizaciones concurrentes de ArrayList no darán resultados correctos.

Si reemplazo el ArrayList con una versión Sincronizada, el programa genera los resultados correctos.

0

Usted podría utilizar en lugar de ArrayList();:

Collections.synchronizedList(new ArrayList()); 

o

new Vector(); 

synchronizedList como de mí porque es preferible:

  • más rápido en 50-100%
  • puede trabajar con Arra ya existente yList
Cuestiones relacionadas