que - transient java
¿Está entrando el bloque sincronizado atómico? (3)
Estás sincronizando sobre el objeto al que apunta ese "objeto", no la variable que contiene el valor.
Sin embargo, como ambas piezas de código se sincronizan en su objeto antes de seguir adelante, está seguro, aunque este es un patrón de diseño deficiente.
Puede encontrar menos confusión si utiliza métodos sincronizados en lugar de bloques de código sincronizados.
Además, solo mi opinión, pero sincronizada (objeto) parece un patrón de diseño MUY pobre. Esa es solo mi opinión, pero nunca hago algo así.
¿Sabes si se garantiza que el bloqueo sincronizado en Java es atómico?
Imagina el siguiente caso
Thread_1,2:
synchronized(object){object.modify();}
(el objeto es una variable compartida)
imagine que thread_M cambiará la referencia a un objeto como
synchronized(object){object = new Object()}
ahora imagine que los hilos 1 y 2 compiten por obtener el objeto de bloqueo
¿Es posible que ocurra lo siguiente?
1. Thread1: leer objeto viejo
2. ThreadM: modificar referencia de objeto y liberar bloqueo de objeto antiguo
3. Thread2: leer nuevo objeto; control de bloqueo; encerrarlo
4. Thread1: check lock (ok porque el viejo objeto fue leído); encerrarlo
ahora ambos hilos tienen un candado y modifican el mismo (nuevo) objeto
Entonces, para especificar mi pregunta, ¿hay algún lugar garantizado que en los pasos sincronizados (objetos) (1 y 4) sean atómicos (como se muestra en el paso 3)?
Puedes reasignar object
mientras estás sincronizado en un object
, pero no puedo pensar en un escenario en el que reasignar un campo utilizado para bloquear sea una buena idea.
Ningún otro hilo podrá adquirir el bloqueo del antiguo valor del object
hasta que el hilo M salga de su bloque sincronizado, pero otro hilo podrá adquirir un bloqueo en el nuevo objeto tan pronto como sea visible para ese hilo.
Las modificaciones hechas por un hilo antes de liberar un bloqueo están garantizadas para ser visibles para los hilos que adquieren el bloqueo después. Pero como está reasignando el bloqueo en sí, es posible que el hilo de adquisición no vea que ha sido modificado y adquiera un bloqueo en el valor anterior. Entonces todavía no verían que el object
ha sido reasignado.
Declarar el object
como una variable volatile
garantizaría que su valor "actual" se use para el bloqueo. Pero no evitaría que dos subprocesos modificaran la misma instancia al mismo tiempo:
- El hilo M adquiere un bloqueo en el valor anterior. El hilo 1 lee el valor anterior.
- El hilo M cambia el valor.
- El hilo M libera el bloqueo del valor anterior. El hilo 2 lee el nuevo valor.
- El subproceso 1 adquiere el bloqueo en el valor anterior. El hilo 2 adquiere un bloqueo en el nuevo valor.
- El hilo 1 lee un nuevo valor. El hilo 2 lee un nuevo valor.
- El hilo 1 modifica el nuevo valor. El hilo 2 modifica el nuevo valor.
Para evitar todo esto, solo crea un objeto separado para bloquear, y nunca lo cambies.
Supongamos que tiene alguna variable, foo
:
Foo foo;
Y supongamos que contiene una referencia a un objeto:
foo = new Foo(...);
Y supongamos que tenemos un bloque synchronized
:
synchronized(foo) {
...
}
El keywoord synchronized
no opera en la variable, foo
, y no opera en las declaraciones en el bloque sincronizado.
Lo único que hace la palabra clave synchronized
aquí es que evita que otros subprocesos se sincronicen en la misma instancia al mismo tiempo.
Si reasigna la variable, foo
para referirse a una instancia diferente mientras el hilo A está dentro del bloque, entonces otro hilo B podrá ingresar al mismo bloque al mismo tiempo porque cada uno de los dos hilos se sincronizará en un diferente ejemplo.