¿Hay una lista concurrente en el JDK de Java?


277

¿Cómo puedo crear una instancia de Lista concurrente, donde puedo acceder a elementos por índice? ¿El JDK tiene alguna clase o método de fábrica que pueda usar?


28
¿Por qué no constructivo? Varios propusieron CopyOnWriteArrayList que no se encuentra en .Net. ¡Puedes decir que ambas preguntas se relacionan entre sí, pero no para cerrar esta!
AlikElzin-kilaka

1
No tengo idea de por qué Jarrod Roberson pensaría que era una buena idea tomar las ediciones detalladas hechas por Stephan y volverlas a la pregunta original, mal redactada. La respuesta de Jarrod sigue siendo perfectamente aceptable. De hecho, CopyOnWriteArrayList es la única clase concurrente que implementa List en el JDK. Desconcertado ...
Matt Passell

10
Debido a que la respuesta aceptada fue a la pregunta original y Stephan formuló una pregunta completamente no relacionada con un montón de código fuente que el póster original no incluyó en ningún lugar cambiando la pregunta por completo, lo que generó más respuestas que sugerían cosas distintas de las Listque el original específicamente dice que es un requisito que se considera vandalismo. Un moderador ya bloqueó la pregunta debido a las personas que se quejan de que las respuestas no responden a esa versión vandalizada de la pregunta.

1
/ locked/ closed/ comentario anterior

8
No hay razón para que esta pregunta se cierre. Pregunta sobre las clases en el JDK, que no es como buscar una biblioteca; Es la base de Java.
maaartinus

Respuestas:


175

Hay una implementación de lista concurrente en java.util.concurrent . CopyOnWriteArrayList en particular.


84
Tenga en cuenta que copia la lista completa en cada inserción, por lo que a menudo es ineficiente.
dfrankow

22
@dfrankow Pero puede más más eficiente si usted está iterando mucho más que va a actualizar.
b1nary.atr0phy

No funciona bien como se muestra aquí. Tengo excepciones a pesar de que solo uso su método addAll y lo leo usando stream. stackoverflow.com/questions/1527519/…
devssh

166

Si no le importa tener acceso basado en índices y solo desea las características de preservación del orden de inserción de una Lista, puede considerar un java.util.concurrent.ConcurrentLinkedQueue . Como implementa Iterable, una vez que haya terminado de agregar todos los elementos, puede recorrer los contenidos utilizando la sintaxis mejorada:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

44
Creo que el enunciado simplificado para ( :) se llama foreach: docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka

2
@ AlikElzin-kilaka Tienes razón. Creo que ese nombre siempre me ha molestado, porque la sintaxis real no incluye la palabra "cada uno", pero actualizaré la respuesta para usar el nombre oficial. :)
Matt Passell

3
@ AlikElzin-kilaka Nitpicking, pero de acuerdo con la versión 8 de JLS se llama "mejorado para la declaración". Lo mismo en el tutorial de Java .
Roland

1
@Roland definitivamente NO es quisquilloso. Hay (ahora) una diferencia entre "para cada uno" y "mejorado para" en Java.
hfontanez

2
@Roland indirectamente. Creo que cambiaron el nombre de "para cada bucle" a "mejorado para" para eliminar la confusión entre Stream.forEach y lo que ahora se conoce como mejorado.
hfontanez

128

Puedes usar Collections.synchronizedList (List) si todo lo que necesitas es una simple sincronización de invocación:

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
El resultado de synchronizedListes "sincronizado" pero no "concurrente". Una cuestión fundamental es que muchas operaciones de Lista, que están basadas en índices, no son atómicas y deben formar parte de una construcción de exclusión mutua más amplia.

66
En mi opinión, preguntar a Vectores más sencillo que Collections.synchronizedList(new ArrayList<Object>()).
Stephan

8
El resultado de synchronizedList es "sincronizado" pero no "concurrente".
Kanagavelu Sugumar

46

Debido a que el acto de adquirir la posición y obtener el elemento de la posición dada, naturalmente requiere cierto bloqueo (no puede hacer que la lista tenga cambios estructurales entre esas dos operaciones).

La idea misma de una colección concurrente es que cada operación por sí sola es atómica y se puede hacer sin bloqueo / sincronización explícitos.

Por lo tanto, obtener el elemento en la posición nde una Listoperación atómica dada no tiene mucho sentido en una situación en la que se anticipa el acceso concurrente.


66
Joachim, creo que has dado en el clavo. Tomemos, por ejemplo, una lista de solo lectura como una lista concurrente. Obtener el elemento en la posición N de la lista no solo tiene sentido, sino que es la cáscara del problema. Entonces, una lista inmutable (L minúscula) sería un buen ejemplo, pero no es una Lista (L mayúscula). CopyOnWriteArrayList es concurrente, pero a muchas personas no les gusta el rendimiento. Una solución a lo largo de las líneas de cuerdas (cuerdas de cuerda) probablemente sería un buen ganador.
johnstosh

1
Muy buen punto. Pero la lista que usará el OP podría tener un uso muy específico. Por ejemplo, puede rellenarse en un entorno concurrente, luego "bloquearse" (lo que sea que signifique) y luego acceder de forma segura por índice. Por lo tanto, en la primera fase de llenado de dicha Lista aún se requerirá una implementación segura para subprocesos. Desafortunadamente, el OP no fue específico sobre cómo se usará la Lista que está buscando.
igor.zh

13

Tienes estas opciones:

  • Collections.synchronizedList(): Se puede envolver cualquier Listaplicación ( ArrayList, LinkedListo una lista tercera parte). El acceso a todos los métodos (lectura y escritura) estará protegido mediante synchronized. Al usar iterator()o mejorar el bucle, debe sincronizarlo manualmente; mientras itera, otros hilos están completamente bloqueados incluso de la lectura. También puede sincronizar por separado para cada una hasNexty las nextllamadas, pero luego ConcurrentModificationExceptiones posible.

  • CopyOnWriteArrayList: es costoso modificarlo, pero esperar para leerlo. Los iteradores nunca arrojan ConcurrentModificationException, devuelven una instantánea de la lista en el momento de la creación del iterador, incluso si la lista es modificada por otro hilo mientras itera. Útil para listas actualizadas con poca frecuencia. addAllSe prefieren las operaciones masivas como las actualizaciones: la matriz interna se copia con menos frecuencia.

  • Vector: muy parecido synchronizedList, pero la iteración también está sincronizada. Sin embargo, los iteradores pueden lanzar ConcurrentModificationException, si el vector es modificado por otro hilo mientras itera.

Otras opciones:

  • Collections.unmodifiableList(): sin bloqueo, seguro para subprocesos, pero no modificable
  • Queueo Dequepodría ser una alternativa si solo agrega / elimina al final de la lista e itera la lista. No hay acceso por índice ni adición / eliminación en lugares arbitrarios. Tienen múltiples implementaciones concurrentes con un mejor rendimiento y un mejor acceso concurrente, pero está fuera del alcance de esta pregunta. También puede echar un vistazo a JCTools , que contienen implementaciones de cola más eficaces especializadas para un solo consumidor o un solo productor.

4

ingrese la descripción de la imagen aquí

CopyOnWriteArrayList es una variante segura de subprocesos de ArrayList en la que todas las operaciones mutativas (agregar, establecer, etc.) se implementan haciendo una copia nueva de la matriz subyacente.

CopyOnWriteArrayList es una alternativa concurrente de la lista sincronizada implementa la interfaz List y es parte del paquete java.util.concurrent y es una colección segura para subprocesos.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList es a prueba de fallas y no arroja ConcurrentModificationException cuando CopyOnWriteArrayList subyacente se modifica durante la iteración, use una copia separada de ArrayList.

Esto suele ser demasiado costoso porque la matriz de copia involucra cada operación de actualización, se creará una copia clonada. CopyOnWriteArrayList es la mejor opción solo para operaciones de lectura frecuente.

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.