ProgramaciónDesarrollador Backend

Describe cómo funciona el bloque synchronized en Java. ¿Cuáles son sus características, cómo elegir un objeto para la sincronización y cómo una elección incorrecta puede llevar a errores?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

synchronized es una palabra clave que permite realizar un acceso seguro a hilos a secciones críticas de código. Puede usarse tanto para un método (por ejemplo, public synchronized void foo()) como para un bloque de código (synchronized(obj) { ... }). Cuando un hilo entra en un bloque synchronized, obtiene el monitor (lock) del objeto. Mientras el monitor esté ocupado, otros hilos no pueden entrar en otro bloque synchronized que use el mismo objeto como monitor.

Características:

  • La sincronización sobre el mismo objeto garantiza la exclusión mutua (solo un hilo puede ejecutar el bloque a la vez).
  • Se puede sincronizar sobre this, sobre un objeto estático (por ejemplo, sobre la clase), o sobre un objeto arbitrario.
  • Si se elige como monitor un objeto accesible a múltiples hilos o, al contrario, un objeto demasiado local, se puede comprometer la seguridad de los hilos.

Ejemplo de elección del objeto de sincronización

public class Counter { private int count; private final Object lock = new Object(); // objeto privado public void increment() { synchronized(lock) { count++; } } }

¿Por qué es mejor usar un lock privado? Porque si se sincroniza sobre un objeto público (por ejemplo, sobre this o sobre una cadena pública), el código externo también puede capturar ese monitor, lo que lleva a bloqueos (deadlocks) o un funcionamiento incorrecto.

Pregunta capciosa.

Pregunta: ¿Qué sucederá si se sincroniza sobre un objeto de tipo String que contiene un valor fijo?

Respuesta: Las cadenas en Java son internadas (los mismos objetos para literales idénticos). Si se sincroniza sobre una cadena como synchronized("lock"), se puede cruzar accidentalmente con otro código que se sincroniza sobre el mismo literal, lo que resultará en un bloqueo inesperado entre partes completamente diferentes del programa.

Ejemplo (no se debe hacer):

synchronized("LOCK") { ... }

Ejemplos de errores reales debido a la falta de conocimiento sobre el tema.


Historia

En un sistema de trading multihilo, se utilizaron objetos públicos para sincronización, y el código externo pudo capturar el lock, lo que llevó a deadlocks temporales entre hilos de diferentes módulos y tiempos de inactividad en la bolsa.


Historia

Un joven desarrollador sincronizó el acceso a una colección sobre un literal de cadena. Otra parte del código también se sincronizó sobre una cadena con el mismo valor. Estos hilos se pusieron en cola uno tras otro, lo que provocó una fuerte desaceleración de la lógica de negocio.


Historia

Para la sincronización, se eligió cada vez un nuevo objeto: synchronized(new Object()) { ... }. Como resultado, la sincronización no funcionó en absoluto, y diferentes hilos accedieron simultáneamente a los datos. Esto solo se descubrió en las pruebas de carga.