Buscar
Social
Ofertas laborales ES
« IDEA vs Eclipse | Main | Tendencias para desarrollo móvil en 2012 »
jueves
mar222012

¿Son los APIs oficiales de Java thread-safe?

Recientemente un amigo me hizo una pregunta (la que da título a esta entrada), yo pensaba que estaba clara para la mayoría de los programadores Java; pero pensando un poco al respecto, me di cuenta de que puede haber no pocas dudas al respecto. Y eso me ha llevado a escribir esta entrada en JavaHispano, con el fin de aclarar hasta donde yo pueda esta pregunta y con el deseo de que otros programadores de esta comunidad aporten su experiencia y conocimientos al respecto y que me ayuden a clarar los míos.

Para aquellos que hayan leído la documentación de Sun/Oracle, la pregunta puede parecer fácil de contestar. Citando esta fuente, podemos leer:

The Java Platform API Specification is a contract between callers and implementations.

The Specification describes all aspects of the behavior of each method on which a caller can rely. It does not describe implementation details, such as whether the method is native or synchronized. The specification should describe (textually) the thread-safety guarantees provided by a given object. In the absence of explicit indication to the contrary, all objects are assumed to be "thread-safe" (i.e., it is permissible for multiple threads to access them concurrently). It is recognized that current specifications don't always live up to this ideal.

Y por lo tanto, deduciremos que efectivamente, y encaso de que no se diga explícitamente lo contrario, las APIs oficiales de Java, son thread-safe. Es decir, podemos concluir que para las clases de los APIs oficiales, no es necesario incluir las llamadas a sus métodos dentro de un bloque synchronized.

Pues "yo no lo haría, foratesro". Se me ocurren al menos un par de argumentos en contra de esta primera intención:

a) ¿Qué pasa con los interfaces definidos por Sun/Oracle que son implementados por terceros (p.ej. DataSource)? ¿Podemos confiar en que han sido implementados de forma "thread-safe"? Yo no confiaría mucho en ello. De hecho, si alguien se toma la molestia de googlear buscando si el método DataSource::getConnection() es thred-safe, verá que no hay forma de saberlo; a mi al menos me ha sido imposible encontrar la respuesta. Hasta donde yo sé: lo era pero ya no, espera que es que Oracle dice que sí, pero el otro dice que depende, es que no es lo mismo si es una aplicación "stand alone" que si corre en un servidor de aplicaciones, pero no es lo mismo un servidor de aplicaciones que otro... Un lío considerable.

b) Esta razón es aún más peliaguda que la anterior. Del párrafo en inglés citado anteriormente, se puede concluir que efectivamente y en un mundo ideal, las APIs oficiales son "thread-safe". Pero la última frase del texto citado admite claramente que esto no siempre ocurre, y no estoy hablando de Swing, del que sabemos perfectamente cómo funciona a este respecto. Por lo que mucho me temo que en más de un caso, la implementación no es "thread-safe".

Por las dos razones anteriores, yo personalmente y salvo que tenga muy claro que una clase o un método de una clase es "thread-safe", siempre incluyo las llamadas a las instancias de los APIs oficiales en bloques synchronized.

Sin embargo, me gustaría saber qué experiencia tienen otros programadores y cómo actúan a este respecto. Invito a todo a aquél que le apetezca a compartirla con los demás dejando sus comentarios en esta "thread" (no me he podido resistir :-))

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (19)

Los Javadocs de cada clase del API indican si la clase de referencia es thread-safe o no.
Lo más recomendable es asegurarse, con la información explícita del Javadoc, antes de usar una clase cualquiera.

Hay otras maneras de sincronizar, aparte de la palabra reservada synchronized, que en según que casos pueden ser más eficientes. El ReentrantLock es uno de ellos.

http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html

marzo 22, 2012 | Registered Commenterchoces

Hola Choces.
Ojalá pudiéramos saber si una clase es o no thread-safe con sólo mirar los JavaDocs, pero eso, como comento, no es tan directo: si el javadoc se dice que sí lo es o que no lo es deberemos fiarnos, pero muchos casos no se dice nada, ni en un sentido ni en el otro.

En cuanto a ReentrantLock estoy de acuerdo contigo (el sub-API "concurrent" incluye otro montón de herramientas útiles a este respecto), pero mis comentarios no pretenden ser un curso sobre thread-safety.

Muchas gracias en cualquier caso por tus comentarios.

marzo 22, 2012 | Registered Commenterpeyrona

No hay duda de que los Javadocs no son "una Biblia", y que carecen de la "infalibilidad ex-cátedra" papal ;)
Pero son un buen punto de partida. Cuando todavía existen dudas, que las habrá, siempre se puede "hojear" el código fuente de la clase en cuestión, y armarse de paciencia.
Normalmente solo se adoptan medidas tan "extremas" cuando aparecen bugs, y se está razonablemente seguro de que no tienen su origen en el código propio.

Otro asunto, como comentas bien, es qué pasa con las implementaciones de terceros. Si sus respectivos Javadocs no lo aclaran, y su código fuente tampoco... ¡Mal vamos!

De todos modos, dentro de las enormes dificultades que tiene escribir código thread-safe, creo que la mayoría de los problemas no proceden del API, sino de un estilo de codificación multitarea descuidado, por no decir algo más grueso :D

marzo 22, 2012 | Registered Commenterchoces

@choces

Ahí lo has clavao... :-)

marzo 22, 2012 | Registered Commenterpeyrona

y por mencionar, que no quede el API Calendar, Date (el java.util y el java.sql) o los DateFormat y derivados, grandes hamijos todos ellos de la thread-safety-idad. Gran parte de las Collection tampoco son synchronized (a no ser que o bien escojamos alguna que lo sea o la envolvamos con un Collections.synchronized*()).

A mi humilde entender, hay que tener los huevos raspados de pelearse con estas cosas para conocerlas, dado que una vez hemos salido del "punto de partida" (aka javadocs) no hay mucho más que hacer salvo pelarse los güitos y devanarse los sesos.

Últimamente estoy mirando cosas de programación funcional, y la verdad me parece la mejor manera de hacer tu código thread-safe + legible...

marzo 22, 2012 | Unregistered CommenterPerry Mason

Aunque sea un poco offtopic, si alguien está interesado en temas de concurrencia probablemente le interese asistir mañana a esta charla:

http://www.javahispano.org/espanha/2012/3/14/charla-detecting-and-fixing-deadlocks-por-heinz-kabutz.html

marzo 22, 2012 | Registered CommenterAbraham

Que interesante Abraham. ¿La charla es gratuita? ¿Habrá webcast?

marzo 22, 2012 | Registered Commenterantoniovl

Aparte de esa charla, para quienes les pille a mano, si se quiere codificar en multitarea, sin "despeñarse" en el intento, es casi imprescindible leer este libro:

Java Concurrency in Practice
By Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea
...............................................
Publisher: Addison Wesley Professional
Pub Date: May 09, 2006
Print ISBN-10: 0-321-34960-1
Print ISBN-13: 978-0-321-34960-6
Pages: 384

marzo 22, 2012 | Registered Commenterchoces

La charla es gratis, y trataremos de grabarla pero no será transmitida en directo.

marzo 22, 2012 | Registered CommenterAbraham

Sin convertir esto en "un curso sobre thread-safety".

:o)

Entre los comentarios de choces y peyrona creo que está todo dicho.

Para mi todas las clases (a priori ) son no thread safe salvo que la documentación y las pruebas demuestren lo contrario.

Dicho esto, no es un problema, es una ventaja, ya que esta en tus manos optimizar el manejo de thread's según te convenga, en lugar de terminar con problemas de desbordamiento por culpa de algo que otro definió como synchronized (o con un nivel de sincronización) según criterios distintos a los de tus necesidades.

Por otro lado la programación multiThread, no es para cualquiera.

marzo 22, 2012 | Registered Commenterefrigerio

A mi el que un metodo este o no sincronizado me suele importar un pimiento porque en la gran mayoria de las ocasiones en las que necesito objetos compartidos entre hilos casi siempre hago mas de una llamada, y el synchronized me sirve tambien para documentar esa situacion-bloque, me da igual que los metodos llamados sean thread-safe.

marzo 23, 2012 | Registered Commenterjmarranz

Yo como Efrigerio sigo el criterio contrario al que dice la especificación, si no menciona explicitamente que es thread-safe entonces voy con mucho ojo.
Que un API sea thread-safe es algo más que simplemente tener el metodo sincronizado. Muchas veces es la diferencia entre tener una instancia por thread o poder compartir una instancia entre todos los threads, así que un pelín si importa.

marzo 23, 2012 | Unregistered CommenterMeAndMyself

@Abraham
Muy interesante esa charla, sobre todo por quién la da.

@Efrigerio
Totalmente de acuerdo, salvo en lo último: sí que es para la inmensa mayoría de programeros Java porque casi todos nos la tenemos que ver con EE: EJBs, Servlets, etc

@jmarranz
Compi, lo siento, pero no me entero de qué querías decir; si pudieras explicarte un poco mejor...

marzo 23, 2012 | Registered Commenterpeyrona

Yo estoy de acuerdo con la afirmación de @efrigerio, porque creo que se refiere a la necesidad de tener una considerable destreza para la codificación en multitarea; en resumen, que no es para principiantes, ni para programadores "ocasionales".

Creo que se está poniendo mucho énfasis en si las clases son seguras, en función de que sus métodos estén sincronizados o no.
El problema de la multitarea es la coherencia de los datos, que son quienes deben "sincronizarse" entre tareas paralelas. El método que se use para la sincronización dependerá de cada caso, teniendo en cuenta que no suele ser buena idea, desde el punto de vista del rendimiento, usar sistemáticamente la palabra clave "synchronized" en la declaración de los métodos.

Un ejemplo habitual, y que suele causar muchos disgustos, es el uso del ConcurrentHashMap, una clase etiquetada como thread-safe. Sin embargo, la "letra pequeña" en los Javadocs advierte que los iteradores no son thread-safe y que el bloque que los incluya debe sincronizarse con algún monitor. El método donde se utilice el iterador puede no usar "synchronized" en su declaración, y la iteración ser perfectamente thread-safe, siempre que se ejecute dentro de un lock.

marzo 23, 2012 | Registered Commenterchoces

@peyrona
EJB, Servlet, Precisamente, lo primero que se hiso con esas especificaciones fue sacarle a la aplicación el manejo de multiThread y mandarlo al context.
Y en mi experiencia mucho de lo de "swing es lento", "funciona mal" o pantallas congeladas, van de un mal manejo de los thread's por el programador y no por culpa de swing.

Quizás lo de "la programación multiThread, no es para cualquiera" haya sonado pedante,
pero como bien argumenta @choces, programar tareas en multiThread sin desbordamientos, sin picos de procesador, con buen consumo de memoria y reutilización de thread es para gente con criterio que sabe lo que hace, y no para un principiante o para aquellos que dicen "lo importante es el negocio, que los lenguajes se aprenden solos".

marzo 23, 2012 | Registered Commenterefrigerio

@efrigerio

"... van de un mal manejo de los thread's por el programador y no por culpa de swing."

Yo no quería mentar la "bicha"; pero ya que lo has hecho, voy a añadir algo más a ese comentario tuyo, con el que no puedo estar más de acuerdo.

No se trata solo de un mal manejo de los threads, sino de la barbaridad de querer ejecutar código dentro del EDT, cuando no se debe, y de ejecutar código Swing fuera del EDT, cuando debería hacerse dentro.
Precisamente la codificación en Swing suele ser un buen indicador de hasta qué punto se comprende bien la multitarea en Java.

marzo 23, 2012 | Registered Commenterchoces

@choces
Creo que peyrona (sin saberlo) con esta nota, toco un nervio y termine haciendo catarsis.

Las de discusiones, fricciones, desgaste y dolores de cabeza que me ha traído la programación multiThread, con gente que no la entienden me ha dejado trastornado.

Bien empleada es una herramienta extraordinaria, mal empleada dan ganas de matar a alguien o de suicidarte por impotencia.

:o(

marzo 23, 2012 | Registered Commenterefrigerio

A petición de peyrona y aunque sea ya un poco tarde, me explico:

Quería decir que suelo tener el siguiente escenario:

synchronized(monitor)
{
obj1.metodo1();
obj2.metodo2();
}

Me da igual si metodo1() y metodo2() son sincronizados o no (o admiten hilos concurrentes en general), y es que aunque ambos fueran sincronizados, es posible que deban hacerse ambas operaciones en el mismo hilo, por lo que a nivel práctico el que sean multihilo ambos o no me da igual.

Por ejemplo, me he encontrado muchas veces con el problema de mantener dos colecciones con los mismos datos pero organizados de dos formas diferentes (para eso las dos listas), me da igual si las dos colecciones están sincronizadas o no, necesito que sólo un hilo acceda a las dos colecciones en cada momento por lo que tengo que usar un bloque synchronized.

Por otro lado está la eterna disyuntiva de compartir un objeto entre hilos o bien crearlo bajo demanda para cada hilo, en el caso de decidir compartir un objeto entre hilos es obvio que debemos saber si los métodos admiten multihilo o no y si no es así sincronizar. De todas formas casi siempre suele dar más problemas (e incluso lentitud) el reutilizar un objeto que crear el objeto bajo demanda, salvo que el objeto que compartimos tenga un coste de creación alto como puede ser un pool de conexiones y la alternativa de crear una conexión por cada acceso a base de datos (en el mismo hilo) tenga un coste inaceptable.

marzo 26, 2012 | Registered Commenterjmarranz

Las API, cuando indican Thread Safe, no indican otra cosa más que el que debiera implementarlas debería respetar esa característica. Como las API muchas veces no proporcionan funcionalidad (son las implementaciones las que la proporcionan) nos encontramos con proveedores (implementadores) que no hacen bien su trabajo al implementar (por ejemplo un Driver de JDBC).
Respecto a la discusión general coincido en todos los puntos:
* La gente por lo general no le presta la atención que merece a los objetos que usan.
* Que la API diga que una implementación debería ser thread safe no quiere decir que lo sea. Depende del trabajo del proveedor.
* Es más, estoy con jmarranz, en general que sea thread safe no garantiza que puedas usarlo aisladamente, dependerá sobre todo de tu código.

Por mi trabajo me toca buscar problemas en aplicaciones que nos entregan diversos proveedores, y veo de todo, desde ignorar por completo el thread safe hasta curarse en salud, y abusar de la sincronización. Creo que muchas veces el coste de crear un objeto frente a la reutilización, compensa por múltiples motivos: facilidad de mantenimiento, de uso, menos problemas en cuanto a concurrencia...
Y gracias a este trabajo, estoy aprendiendo sobre este tema a marchas forzadas.

Cuanto más sé, más pequeña me parece mi parcela del conocimiento.

abril 8, 2012 | Registered CommenterRamón Rial

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>