2009-10-19 15 views
8

Esta pregunta se relaciona con colecciones de Java, específicamente Hashtable y Vector, pero también pueden aplicarse en cualquier otro lugar.Programación de interfaces y colecciones sincronizadas

He leído en muchos lugares qué tan bueno es programar interfaces y estoy de acuerdo 100%. La capacidad de programar a una interfaz de lista, por ejemplo, sin tener en cuenta la implementación subyacente es sin duda útil para desacoplar y probar. Con colecciones, puedo ver cómo una ArrayList y una LinkedList son aplicables bajo diferentes circunstancias, dadas las diferencias con respecto a la estructura interna de almacenamiento, los tiempos de acceso aleatorio, etc. Sin embargo, estas dos implementaciones pueden usarse bajo la misma interfaz ... que es genial.

Lo que no puedo ubicar es cómo ciertas implementaciones sincronizadas (en particular, Hashtable y Vector) se ajustan a estas interfaces. Para mí, no parecen ajustarse al modelo. La mayoría de las implementaciones de estructura de datos subyacentes parecen variar en la forma en que se almacenan los datos (LinkedList, Array, árbol ordenado, etc.), mientras que la sincronización trata las condiciones (condiciones de bloqueo) bajo las cuales se puede acceder a los datos. Veamos un ejemplo donde un método devuelve una colección Mapa:

public Map<String, String> getSomeData(); 

Vamos a suponer que la aplicación no se preocupa en absoluto con la concurrencia. En este caso, operamos en cualquier implementación que regrese el método a través de la interfaz ... Todo el mundo está contento. El mundo es estable.

Sin embargo, ¿qué pasa si la aplicación ahora requiere atención en el frente de concurrencia? Ahora no podemos operar sin tener en cuenta la implementación subyacente: Hashtable estaría bien, pero otras implementaciones deben ser atendidas. Consideremos 3 escenarios:

1) Haga cumplir la sincronización utilizando bloques de sincronización, etc. al agregar/eliminar con la colección. Sin embargo, ¿no sería esto exagerado en caso de que se devuelva una implementación sincronizada (Hashtable)?

2) Cambie la firma del método para devolver Hashtable. Esto, sin embargo, nos vincula fuertemente a la implementación de Hashtable, y como resultado, las ventajas de programar en una interfaz se tiran por la ventana.

3) Haga uso del paquete concurrente y cambie la firma del método para devolver una implementación de la interfaz ConcurrentMap. Para mí, esto parece ser el camino a seguir.

Básicamente, parece que ciertas implementaciones sincronizadas son un poco inadaptadas dentro del marco de colecciones porque, al programar interfaces, el problema de sincronización casi obliga a pensar en la implementación subyacente.

¿Me falta completamente el punto aquí?

Gracias.

+2

Creo que la clave es que añadir unas pocas palabras clave "sincronizadas" aquí o allá no hace que su programa sea seguro para subprocesos. Incluso con colecciones 'java.util.concurrent', desea mantenerlas como detalles privados de implementación. –

Respuesta

6

1) Sí, va a ser excesiva
2) correcta, que no se debe hacer
3) depende de la situación.

La cosa es que, como ya saben, la programación de la interfaz de describir lo que hace la aplicación (no cómo lo hace, eso es la implementación)

sincronización fue retirado de las implementaciones posteriores (recuerde, Vector y Hastable son anteriores a java 1.2 más tarde llegaron ArrayList y HasMap que no estaban sincronizados, pero todos ellos implementaron la interfaz Lista y Mapa, respectivamente), ya que resultan en una penalización de rendimiento debido a la sincronización excesiva. Por ejemplo, si usa un vector en un solo hilo, todavía tiene sincronización dentro de ese único hilo.

Compartir una estructura de datos entre varios subprocesos es algo que debe tenerse en cuenta cuando diseña la aplicación. Allí elegirá los métodos que utilizará y elegirá quién es responsable de mantener limpio el estado de los datos.

Aquí es donde puede elegir entre la opción 1 o 3 que mencionó. ¿Habría una sincronización manual? ¿Deberíamos usar una interfaz sincronizada? ¿Qué versión vamos a apoyar, etc, etc

Por ejemplo, si tienes que elegir 1, también puede en su diseño rechazar ciertas implementaciones (es decir, el vector)

sincronización de datos no es algo que sucede por "suerte" que realmente tiene que diseñar para que suceda correctamente y no causa más problemas que los que soluciona.

Durante este diseño, debe prestar atención a las opciones (las implementaciones) y/o la infraestructura subyacente que usará.

La manera más fácil de evitar la sincronización excesiva es utilizar datos inmutables y no compartir sus datos con otros hilos.

Algo muy similar a la primera ley de la computación en la distribución por Martin Fowler:

"Por lo tanto, llegar a mi primera ley de Diseño de objetos distribuida: No distribuya sus objetos."

¿Sería la primera ley de las aplicaciones multitarea ser:

Primera ley de las aplicaciones multitarea: no compartir sus datos?

:)

Nota final: la clase Collections proporciona la versión "sincronizado" de algunas interfaces:

Synchronized List
Synchronized Map
Synchronized Set

0

de Vector y Hashtable son anteriores paquete de concurrencia actual que se ha añadido en el JDK 5. En el momento Vector fue escrito, la gente pensó que era una buena idea para que sea sincronizado, entonces es probable que chocan contra la pared rendimiento en el uso empresarial de Java. La concurrencia es ciertamente una de esas situaciones en las que la modularidad de código a interfaz no siempre funciona.

+1

Es un poco más extenso que eso: 1.4 introdujo un marco de colecciones actualizado con java.util.Collections.synchronizedMap y amigos que se recomienda utilizar en lugar de Hashtable. Y luego 1.5 trajo todo el paquete concurrente. Pero Hashtable todavía está plagado de bibliotecas estándar y probablemente lo sea cada vez más ... El "enfoque Java" parece ser que la sincronización es un detalle de implementación, y los requisitos para ello deben comunicarse a través de documentación no a través de declaraciones API . – araqnid

+0

Secundado. Ambas clases son históricas, y creo que datan de Java 1.0. Estoy seguro de que datan de 1.2, pero no ven las antiguas API en línea. –

+0

@araqnid, el camino del "enfoque Java" está pintado con sangre y cicatrices de rendimiento, concurrencia, etc. a lo largo de los años. Especialmente desde que despegó en el uso del lado del servidor, la escala se ha inclinado hacia la inyección de la dependencia, código a interfaz, arquitectura astronauta, en comparación con el simple "nuevo Vector();" –

1

Lo que está luchando es el hecho de que en un entorno de subprocesos múltiples, un cliente no puede usar ingenuamente un objeto que tiene un estado mutable y compartido. La interfaz de colección, por sí sola, no le dice nada sobre cómo el objeto se puede usar de forma segura. Devolver un ConcurrentMap ayuda a proporcionar información adicional, pero solo para ese caso en particular.

Normalmente, debe comunicar los problemas de seguridad del subproceso por separado en la documentación (por ejemplo, javadoc) o mediante el uso de anotaciones personalizadas como se describe en Java Concurrency in Practice. El cliente del objeto devuelto tendrá que usar su propio mecanismo de bloqueo o uno proporcionar. La interfaz suele ser ortogonal a la seguridad de la rosca.

No es un problema si el cliente sabe que todas las implementaciones son de las implementaciones simultáneas, pero esa información no es comunicada por la interfaz misma.

Cuestiones relacionadas