Errores comunes al utilizar hilos en Java

Índice de Contenido
  1. Espera no sincronizada
  2. Condiciones de salida no sincronizadas
  3. Lectura y escritura simultáneas
  4. Bloqueo de sincronización
  5. Bloqueo de doble verificación
  6. Conclusión

Espera no sincronizada

Este es un error que todos suelen encontrar la primera vez que intentan usar el método wait. En el Lista A se muestra una llamada al método wait en un objeto arbitrario. El código se compila y se ejecuta bien hasta llegar a la llamada a wait, momento en el que se lanza una IllegalMonitorStateException. Antes de llamar al método wait en un objeto, primero se debe obtener el monitor de ese objeto encerrando la llamada a wait en un bloque sincronizado. Un ejemplo de esto se muestra en el Lista B. Afortunadamente, este error es fácil de detectar; la excepción lo revela. La mayoría de los errores en hilos no son tan amigables, ya que fallan de manera intermitente o generan bloqueos silenciosos. Veamos algunos otros errores a continuación.

Condiciones de salida no sincronizadas

Un uso común de los hilos es tener un hilo de despacho o procesador que se ejecuta en un bucle infinito esperando a que otro hilo externo modifique el estado de un objeto mutuamente accesible. Sin embargo, si no se toma precaución para sincronizar el objeto al que ambos hilos acceden, pueden ocurrir bloqueos sutiles. En el Lista C se muestra un hilo de procesador que se ejecuta en un bucle hasta que la variable booleana done se establece en verdadero. Es evidente que el autor del código espera que un hilo externo cambie el valor de la bandera done a verdadero para finalizar el bucle al final de su iteración actual.

Pero podría surgir un problema con la propagación del estado de la variable booleana done entre los límites de los hilos. El modelo de memoria de Java no garantiza que los cambios realizados en done en un hilo se reflejen en otro. Por lo tanto, es posible que el hilo externo cambie el estado de la bandera, pero el bucle while en el otro hilo se ejecute infinitamente. Desarrolladores experimentados podrían decir que han utilizado código similar al que se muestra en el Lista C y que funciona bien. Tienen razón; por lo general, funciona bien. Sin embargo, no hay garantía de que siempre funcione como se espera, y todo desarrollador experimentado sabe cómo se aplica la ley de Murphy a eventos importantes y a fallas intermitentes.

El modelo de memoria de Java requiere que el estado de una variable sea correcto después de encontrar una barrera de sincronización. Esto se puede utilizar para eliminar el riesgo de un hilo de procesador que se ejecuta infinitamente, como se muestra en el Lista D. El programador experimentado en Java podría preguntarse si la palabra clave volatile podría usarse para solucionar el mismo problema. La palabra clave volatile fue creada para este tipo de situaciones y se pretendía forzar la lectura de memoria al acceder a datos obsoletos en caso de que una variable se actualizara en otro hilo. Desafortunadamente, la Especificación del Lenguaje Java no fue suficientemente detallada al abordar el funcionamiento de volatile, y muchas máquinas virtuales ignoran por completo la palabra clave.

Lectura y escritura simultáneas

Me llevó mucho tiempo creer que esto era posible. Es posible que sepas que las colecciones de Java no están sincronizadas a nivel de método. Por lo tanto, los métodos sincronizados en colecciones dan una falsa sensación de seguridad con respecto a la modificación simultánea de los datos contenidos en ellas. También puede que sepas que si vas a tener dos hilos modificando el mismo objeto, debes asegurarte de que no lo hagan al mismo tiempo. Sin embargo, para algunas colecciones, debes tener cuidado de asegurarte de que ni una lectura ni una escritura ocurran al mismo tiempo. Es posible que las modificaciones realizadas en el almacenamiento interno de datos de la colección no sean atómicas por naturaleza, y mientras el hilo de escritura modifica la colección, el hilo de lectura puede observar resultados indefinidos.

Por ejemplo, examina el código en el Lista E. Dado que solo uno de los dos hilos escribe en el HashMap, parecería a primera vista que no se necesita sincronización. Sin embargo, es posible que durante la acción put en el HashMap, un get encuentre el HashMap en un estado inconsistente.

Análisis de rendimiento para sitios web Java: una guía completa para arquitectos Java

El Lista F muestra el mismo código con un bloque sincronizado agregado. Observa que el put y el get ya no pueden ocurrir al mismo tiempo.

Bloqueo de sincronización

Cuando los datos de dos fuentes de datos deben mantenerse coherentes en una aplicación multihilo, es necesario obtener el bloqueo de sincronización para ambos objetos de almacenamiento de datos antes de realizar cualquier cambio. Se debe tener cuidado para asegurarse de que todo código que intente mantener ambos monitores simultáneamente los obtenga en el mismo orden. Si, como se muestra en el Lista G, un hilo anida sus bloques de sincronización en un orden contrario al de otro hilo que compite por los mismos monitores, puede ocurrir un bloqueo. Cada hilo queda atrapado en el punto indicado por el comentario de bloqueo y espera la liberación del monitor que el otro hilo ya tiene.

Una versión más sutil de este problema se muestra en el Lista H. Recuerda que los métodos sincronizados son simplemente bloques sincronizados estándar que rodean todo el método utilizando el objeto referenciado localmente por la palabra reservada this para el bloqueo. Afortunadamente, una vez encontrado, la solución a estos bloqueos es tan simple como reordenar la sincronización para uno de los hilos.

Bloqueo de doble verificación

No puedo dejar de mencionar el problema de enhebrado de clases que se encuentra en lo que se ha denominado "el Doble Chequeo Idiomático". Bill Pugh ha escrito un excelente artículo explicando este problema de enhebrado más común y contra intuitivo.

Conclusión

El enhebrado puede ser complicado. Pero una vez que has cometido cada error una vez, puedes dejar de lado cualquier temor asociado con el código multihilo y comenzar a usarlo más y más. Un código suficientemente enhebrado puede, bajo las condiciones adecuadas, dejar el tiempo de ejecución del programa completamente limitado por operaciones de E/S, lo cual es una excelente manera de responder a cualquier acusación sobre la lentitud de Java.

Java Management Extensions (JMX): El estándar de Java para administrar recursos de aplicaciones

En Newsmatic nos especializamos en tecnología de vanguardia, contamos con los artículos mas novedosos sobre Desarrollo, allí encontraras muchos artículos similares a Errores comunes al utilizar hilos en Java , tenemos lo ultimo en tecnología 2023.

Artículos Relacionados

Subir

Utilizamos cookies para mejorar su experiencia de navegación, mostrarle anuncios o contenidos personalizados y analizar nuestro tráfico. Al hacer clic en “Aceptar todo” usted da su consentimiento a nuestro uso de las cookies.