Synchronized: qué es, principio de funcionamiento y uso en Java

Autor: IT Sectr Publicado: 2026-03-19 Tiempo de lectura: 8 min

Synchronized es un mecanismo de sincronización integrado en el lenguaje Java que proporciona acceso exclusivo a secciones críticas de código. Según Oracle, 2024, el modificador synchronized garantiza que solo un hilo puede ejecutar el método o bloque marcado en un momento específico. Este mecanismo se basa en monitores, un concepto fundamental de los sistemas operativos que asegura el correcto funcionamiento de aplicaciones multihilo de todos los niveles de complejidad.

Puntos clave

  • Synchronized — palabra clave de Java para acceso seguro a datos entre hilos.
  • Monitor de objeto — el mecanismo interno en el que se basa la sincronización.
  • Método synchronized bloquea todo el método a nivel de instancia o clase.
  • Bloque synchronized permite sincronizar solo una parte del código.
  • Deadlock — uno de los principales problemas con la sincronización anidada.

¿Qué es synchronized?

Synchronized es una palabra clave en Java que garantiza que solo un hilo ejecute una sección protegida de código en un momento dado, evitando la corrupción de datos durante el acceso concurrente. Apareció en la primera versión de Java y sigue siendo la forma más sencilla de garantizar la seguridad entre hilos para desarrolladores de cualquier nivel.

Definición y rol en Java

El modificador synchronized resuelve dos tareas: exclusión mutua y visibilidad de cambios. Cuando un hilo sale de un bloque synchronized, todos los cambios son visibles para otros hilos que entren en un bloque sincronizado en el mismo objeto.

Synchronized se puede aplicar a un método completo o a un bloque de código arbitrario especificando un objeto monitor. En ambos casos, la JVM inserta instrucciones monitorenter y monitorexit a nivel de bytecode.

Razones de su aparición

En aplicaciones multihilo sin sincronización, se produce una condición de carrera (race condition) cuando dos hilos modifican simultáneamente los mismos datos, dando lugar a resultados impredecibles. Synchronized se convirtió en la primera y principal herramienta de Java para combatir este problema, proporcionando una sintaxis declarativa simple accesible a cualquier desarrollador.

¿Cómo funciona synchronized?

El mecanismo synchronized se basa en el concepto de monitor, un primitivo de sincronización de alto nivel incorporado en cada objeto Java. El monitor se asocia a un objeto cuando se usa por primera vez un bloque synchronized sobre él.

Monitor de objeto

Cada objeto en Java tiene un monitor asociado. Cuando un hilo entra en un bloque synchronized, adquiere el monitor del objeto. Si el monitor ya está ocupado por otro hilo, el hilo se bloquea hasta que se libera. En bytecode, esto corresponde al par de instrucciones monitorenter y monitorexit.

Estados de bloqueo (biased locking)

La JVM optimiza synchronized a través de varios niveles: biased locking (bloqueo sesgado) para acceso de un solo hilo, lightweight locking (bloqueo ligero) para baja contención, y heavyweight locking (bloqueo pesado) para contención intensa con participación del SO. Estos niveles mejoran el rendimiento sin cambiar el código.

java
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

Regla happens-before

Synchronized establece una relación happens-before: todas las acciones en un hilo antes de salir de un bloque synchronized son visibles para otro hilo después de entrar en un bloque sincronizado en el mismo objeto. Esto garantiza no solo exclusión mutua sino también consistencia de datos para todos los hilos.

Método synchronized vs bloque

Java ofrece dos formas de aplicar synchronized: a nivel de método y a nivel de bloque. La elección entre ellos afecta al rendimiento y la granularidad de la sincronización.

Método synchronized

Marcar un método con el modificador synchronized lo sincroniza automáticamente en la instancia actual (para métodos de instancia) o en el objeto Class (para métodos estáticos). Es la forma más simple de garantizar exclusión mutua, pero a menudo es excesiva si la sección crítica constituye solo una pequeña parte del método y el resto del código no requiere sincronización.

Bloque synchronized

Un bloque synchronized ofrece control preciso: especificas el objeto monitor y sincronizas solo la sección necesaria, dejando el resto del método fuera del bloqueo. Esto minimiza el tiempo de retención del monitor y mejora el rendimiento general de la aplicación en un entorno multihilo, ya que otros hilos pueden ejecutar código no relacionado en paralelo sin esperar la liberación del monitor.

java
class DataProcessor {
    private final Object lock = new Object();

    public void process() {
        // código fuera de la sección crítica - sin sincronización
        prepareData()

        synchronized (lock) {
            // solo este bloque está protegido
            updateSharedState()
        }

        // continuación sin bloqueo
        cleanup()
    }
}
CriterioMétodo synchronizedBloque synchronized
Monitorthis (instancia) o Classcualquier objeto
Granularidadmétodo completosolo el código necesario
Legibilidadaltamedia
Rendimientomenor para métodos grandesmayor para secciones críticas pequeñas

Synchronized en Android

En el desarrollo de Android, synchronized se usa ampliamente para proteger SharedPreferences, el acceso a bases de datos y componentes de la interfaz de usuario. Sin embargo, su uso en el hilo principal está totalmente desaconsejado debido al riesgo de congelación de la interfaz.

Uso con SharedPreferences

SharedPreferences en Android proporciona seguridad básica entre hilos, pero al editar desde varios hilos a través de Editor, puede ser necesaria una sincronización externa. Un bloque synchronized con un objeto de bloqueo separado garantiza la consistencia de los cambios.

kotlin
class PreferencesManager(private val prefs: SharedPreferences) {
    private val lock = Any()

    fun writeToken(token: String) {
        synchronized (lock) {
            prefs.edit()
                .putString("auth_token", token)
                .apply()
        }
    }
}

Limitaciones en aplicaciones Android

La principal limitación de synchronized en Android es el bloqueo de hilo. A diferencia de las corrutinas con Mutex, synchronized bloquea el hilo del sistema por completo. En el hilo principal, esto causa ANR. En el desarrollo moderno de Android, se recomienda reemplazar synchronized por corrutinas (suspend Mutex) o tipos atómicos (AtomicInteger).

Alternativas a synchronized

Java y Kotlin modernos ofrecen varias alternativas a synchronized, cada una resolviendo los mismos problemas con menos limitaciones o mejor rendimiento.

Lock de java.util.concurrent

La interfaz Lock con las implementaciones ReentrantLock y ReadWriteLock proporciona tiempos de espera, espera interrumpible y múltiples colas Condition. Es más flexible que synchronized pero requiere liberación explícita en finally, lo que aumenta el riesgo de error si se olvida el unlock.

Clases atómicas

AtomicInteger, AtomicLong, AtomicReference y otras clases utilizan algoritmos Lock-Free basados en CAS (Compare-And-Swap). Son significativamente más rápidos que synchronized en escenarios de contención moderada porque no bloquean hilos sino que realizan reintentos optimistas sin requerir cambios de contexto del núcleo del SO.

ThreadLocal y seguridad entre hilos

ThreadLocal proporciona un enfoque alternativo: cada variable ThreadLocal está aislada dentro de un único hilo y no requiere sincronización para lectura y escritura. Esto elimina por completo la necesidad de synchronized para datos que no deben compartirse entre hilos. ThreadLocal se usa activamente en frameworks (Spring, Hibernate) para almacenar contexto de transacciones y sesiones.

Corrutinas y enfoque compose

En proyectos Kotlin para Android, una alternativa a synchronized es Mutex de kotlinx.coroutines. No bloquea el hilo del sistema operativo sino que suspende la corrutina hasta que se libera el bloqueo, lo que permite un uso eficiente de los hilos del pool y evita ANR durante esperas prolongadas de liberación de recursos.

kotlin
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

val mutex = Mutex()
var counter = 0

suspend fun safeIncrement() {
    mutex.withLock {
        counter++
    }
}

Rendimiento de synchronized

El rendimiento de synchronized ha cambiado significativamente en las versiones recientes de Java. Antes se consideraba un mecanismo “pesado”, pero las JVM modernas han eliminado la mayor parte de la sobrecarga mediante optimizaciones avanzadas del compilador JIT. Veamos en detalle cómo la máquina virtual acelera el código sincronizado en tiempo de ejecución.

Biased Locking y Lock Coarsening

El compilador JIT de la JVM aplica varias optimizaciones: biased locking elimina la sincronización si el bloqueo siempre lo adquiere el mismo hilo; lock coarsening fusiona bloques synchronized adyacentes en uno solo; lock elimination elimina la sincronización si el objeto solo es accesible por un hilo. Estas optimizaciones hacen que synchronized sea prácticamente gratuito con baja contención.

Medición de contención y selección de optimización

La JVM determina el nivel de contención para cada objeto: cuando no hay contención, se activa biased locking; cuando aparece un segundo hilo, el bloqueo pasa a modo ligero con espera activa (spin); y solo durante espera prolongada se escala a pesado con un mutex del sistema. Esta escalada ocurre automáticamente, y el desarrollador no necesita elegir manualmente una estrategia.

Comparación con Lock y clases atómicas

En benchmarks modernos (Java 17+), synchronized muestra un rendimiento comparable a ReentrantLock con contención baja y moderada. Con alta contención, Lock puede tener ventaja gracias a una cola de espera más eficiente con soporte de timeouts e interrupción. Para sistemas de alta carga donde la contención es constante, ReentrantLock con modo fair ofrece un comportamiento más predecible.

Las clases atómicas (AtomicInteger, AtomicReference) siguen siendo las más rápidas para contadores y banderas simples gracias a la implementación Lock-Free basada en CAS. No bloquean hilos en absoluto: en caso de conflicto, la operación simplemente se reintenta en un bucle. Esto proporciona un aumento de rendimiento de 3 a 5 veces en comparación con synchronized en operaciones de incremento de contador con 4-8 hilos.

Preguntas frecuentes

¿Cuál es la diferencia entre synchronized y volatile?

Synchronized proporciona tanto exclusión mutua como visibilidad. Volatile solo garantiza visibilidad de cambios: escribir en una variable volatile es visible para todos los hilos pero no evita la modificación simultánea, es decir, no protege contra condiciones de carrera.

¿Puede synchronized causar un deadlock?

Sí, es posible un deadlock con sincronización anidada usando diferentes órdenes de monitores. Por ejemplo, un hilo llama a synchronized(a) { synchronized(b) }, mientras que otro llama a synchronized(b) { synchronized(a) }. Evita bloques synchronized anidados o fija un orden de monitores consistente.

¿Qué es un monitor en Java?

Un monitor es un mecanismo de sincronización asociado a cada objeto Java. Garantiza que solo un hilo ejecuta código synchronized en ese objeto. El monitor incluye un bloqueo, una cola de espera y un conjunto de hilos esperando notificación mediante wait/notify.

¿Es Lock más rápido que synchronized?

En versiones modernas de Java (17+), synchronized no va por detrás de Lock en rendimiento gracias a las optimizaciones JIT (biased locking, lock coarsening). Lock se prefiere no por velocidad sino por capacidades adicionales: timeouts, espera interrumpible y múltiples colas Condition.

¿Cómo funciona synchronized con métodos estáticos?

Un método estático synchronized usa el monitor del objeto Class de la clase dada, no de la instancia. Esto significa que la sincronización se aplica a todas las instancias de la clase. Los métodos synchronized no estáticos y estáticos usan diferentes monitores y no se bloquean entre sí.

Resumen

  • Synchronized — mecanismo de sincronización integrado de Java basado en monitores.
  • Monitor de objeto — estructura interna de la JVM que garantiza exclusión mutua.
  • Métodos wait/notify se usan solo dentro de bloques o métodos synchronized.
  • Bloque synchronized es preferible a un método por su granularidad de sincronización más fina.
  • Happens-before garantiza visibilidad de cambios entre hilos al sincronizar en el mismo objeto.
  • Deadlock — el principal riesgo con sincronización anidada usando diferente orden de monitores.
  • Alternativas — Lock, clases atómicas y corrutinas con suspending Mutex.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también