Está en la página 1de 4

Capítulo 3.

Compartir objetos declarados al principio del Capítulo 2 que escribir programas


correctos y simultáneos se refiere principalmente a la gestión del acceso a objetos compartidos,
mutables tate. Este capítulo se refería a la utilización de la sincronización para evitar el acceso a
múltiples subprocesos de los mismos datos al mismo tiempo; este capítulo examina las técnicas
para compartir y publicar objetos a los que se puede acceder de forma segura a través de
múltiples subprocesos. Hemos visto cómo los bloqueos y métodos sincronizados pueden asegurar
que la operación se ejecute de forma automática, pero con una concepción errónea común, que
se sincroniza en su totalidad con la anatomía o con la delimitación de "secciones críticas".No sólo
queremos evitar que un hilo de rosca modifique el estado de un objeto cuando otro lo está
usando, sino también asegurarnos de que cuando se modifica el estado de un objeto, otros hilos
de rosca pueden ver en realidad los cambios que se han hecho, pero sin sincronización, esto no
puede ocurrir. Puede asegurarse de que los objetos se publiquen con seguridad, ya sea utilizando
la sincronización de explícitos o aprovechando la sincronización incorporada en las clases de la
biblioteca.

3.1. Visibilidad La visibilidad es sutil porque las cosas que no pueden ser contadas como
intuitivas.InasingleͲthreadedenvironment, si usted escribe debido a una variable variable y luego
lee esa variable sin escribir nada, puede esperar que se le devuelva el mismo valor.Esto parece ser
algo natural.En general, no hay garantía de que el hilo que se está leyendo tendrá un valor escrito
por otro hilo a tiempo, o incluso en todos los casos. Dos hilos, el hilo principal y el hilo principal,
acceden a las variables compartidas listas y con un número, el hilo principal inicia el hilo principal y
el hilo superior a 42 y el hilo inferior a 42 y al final a la verdad.Si bien puede parecer obvio que No-
Visibilidad imprimirá42, de hecho es posible que imprima cero, o que nunca termine todo! debido
a que no utiliza la sincronización adecuada, no hay garantía de que los valores deady y el número
escrito por el tema en cuestión sean visibles en el hilo superior.

Incluso de manera más extraña, NoVisibility podría imprimir, ya que la escritura de la lectura
podría hacerse visible a la de la lectura antes de escribir el número, un fenómeno conocido como
orden de pedido.No hay garantía de que las operaciones en un hilo de rosca se realicen en el
orden dado por el programa, siempre que no se detecte una orden desde el hilo de rosca
Ͳevenifthereorderingisapparenttootherthreads.[1] Cuando el hilo de rosca escribe el primer
número y se hace sin sincronización, el hilo del lector podría ver que esas escrituras suceden en el
orden opuesto Ͳ o en absoluto.
En ausencia de sincronización, el compilador, el procesador y el tiempo de ejecución pueden hacer
algunas cosas muy extrañas

orden en que las operaciones parecen ejecutarse. Los intentos de razonar sobre el orden en que
las acciones de memoria "deben" suceder en programas multiproceso insuficientemente
sincronizados seguramente serán incorrectos.

NoVisibility es tan simple como un programa concurrente puede obtener dos hilos y dos variables
compartidas, y aun así es demasiado fácil llegar a conclusiones erróneas sobre lo que hace o
incluso si terminará. Razonar sobre programas concurrentes insuficientemente sincronizados es
prohibitivamente difícil. Todo esto puede sonar un poco aterrador, y debería. Afortunadamente,
hay una manera fácil de evitar estos problemas complejos: utilice siempre la sincronización
adecuada siempre que los datos se compartan entre subprocesos.

3.1.1. Datos obsoletos

NoVisibility demostró una de las formas en que los programas insuficientemente sincronizados
pueden causar resultados sorprendentes: datos obsoletos. Cuando el hilo del lector examina listo,
puede ver un valor desactualizado. A menos que se use la sincronización cada vez que se accede a
una variable, es posible ver un valor obsoleto para esa variable. Peor aún, la obsolescencia no es
todo o nada: un subproceso puede ver un valor actualizado de una variable, pero un valor
obsoleto de otra variable que se escribió primero.

Cuando la comida está rancia, por lo general todavía es comestible, solo que menos agradable.
Pero los datos obsoletos pueden ser más peligrosos. Si bien un contador de visitas fuera de fecha
en una aplicación web puede no ser tan malo, [2] los valores obsoletos pueden causar graves fallas
de seguridad o de vida. En NoVisibility, los valores obsoletos pueden hacer que imprima el valor
incorrecto o evitar que el programa finalice. Las cosas pueden complicarse aún más con valores
obsoletos de referencias de objetos, como los punteros de enlace en una implementación de lista
vinculada. Los datos obsoletos pueden causar fallas graves y confusas, como excepciones
inesperadas, estructuras de datos corruptas, cálculos inexactos y bucles infinitos.

MutableInteger en el Listado 3.2 no es seguro para subprocesos porque se accede al campo de


valor desde get y set sin sincronización. Entre otros peligros, es susceptible a valores obsoletos: si
un subproceso llama al conjunto, otros subprocesos que reciben pueden o no ver esa
actualización.

Podemos hacer que el subproceso MutableInteger sea seguro sincronizando el captador y el


definidor como se muestra en SynchronizedInteger en el Listado 3.3. Sincronizar solo el setter no
sería suficiente: los subprocesos que llaman a get aún podrían ver valores obsoletos.
3.1.2. Operaciones no atómicas de 64 bits

Cuando un hilo lee una variable sin sincronización, puede ver un valor obsoleto, pero al menos ve
un valor que fue colocado allí por algún hilo en lugar de algún valor aleatorio. Esta garantía de
seguridad se denomina seguridad de aire fino.

La seguridad del aire exterior se aplica a todas las variables, con una excepción: las variables
numéricas de 64 bits (dobles y largas) que no se declaran volátiles (consulte la Sección 3.1.4). El
modelo de memoria Java requiere que las operaciones de recuperación y almacenamiento sean
atómicas, pero para las variables largas y dobles no volátiles, la JVM puede tratar una lectura o
escritura de 64Ͳbit como dos 32Ͳ separadas para recuperar los 32 bits altos de un valor y los 32
bits bajos de otro. [3] Por lo tanto, incluso si no le importan los valores obsoletos,

no es seguro usar variables compartidas largas y dobles mutables en programas multiproceso a


menos que sean declaradas volátiles o protegidas por un bloqueo. operaciones de bit Si las
lecturas y escrituras ocurren en diferentes hilos, por lo tanto, es posible leer un archivo no volátil

3.1.3. Bloqueo y Visibilidad

El bloqueo intrínseco se puede utilizar para garantizar que un hilo vea los efectos de otro de
manera predecible, como se ilustra en la Figura 3.1. Cuando el hilo A ejecuta un bloque
sincronizado, y posteriormente el hilo B entra en un bloque sincronizado protegido por el mismo
bloqueo, los valores de las variables que eran visibles para A antes de liberar el bloqueo

están garantizados para ser visibles para B al adquirir la cerradura. En otras palabras, todo lo que A
hizo en o antes de un bloque sincronizado es visible para B cuando ejecuta un bloque sincronizado
protegido por el mismo bloqueo. Sin sincronización, no existe tal garantía.
Ahora podemos dar la otra razón para que la regla requiera que todos los hilos se sincronicen en el
mismo bloqueo al acceder a una variable mutable compartida, para garantizar que los valores
escritos por un hilo sean visibles para otros hilos. De lo contrario, si un hilo lee una variable sin
mantener el bloqueo apropiado, podría ver un valor obsoleto.

El bloqueo no se trata solo de exclusión mutua; También se trata de la visibilidad de la memoria.


Para garantizar que todos los hilos vean los valores más actualizados de las variables mutables
compartidas, los hilos de lectura y escritura deben sincronizarse en un bloqueo común.

3.1.4. Variables volátiles

El lenguaje Java también proporciona una forma alternativa, más débil de sincronización, variables
volátiles, para garantizar que las actualizaciones de una variable se propaguen de manera
predecible a otros subprocesos. Cuando un campo se declara volátil, se notifica al compilador y al
tiempo de ejecución que esta variable se comparte y que las operaciones en él no deben
reordenarse con otros

operaciones de memoria Las variables volátiles no se almacenan en caché en registros o en cachés


donde se ocultan de otros procesadores, por lo que la lectura de una variable volátil siempre
devuelve la escritura más reciente de cualquier hilo.

Una buena manera de pensar sobre las variables volátiles es imaginar que se comportan más o
menos como la clase SynchronizedInteger en el Listado 3.3, reemplazando las lecturas y escrituras
de la variable volátil con llamadas para obtener y establecer.

[4] Sin embargo, acceder a un volátil

La variable no realiza ningún bloqueo y, por lo tanto, no puede hacer que el hilo en ejecución se
bloquee, lo que hace que las variables volátiles sean un mecanismo de sincronización de peso más
ligero que el sincronizado

También podría gustarte