La concurrencia de subprocesos múltiples se puede lograr en JAVA mediante la declaración sincronizada. Al utilizar bloques de código sincronizados, la JVM garantiza que solo un subproceso pueda mantener el bloqueo en un determinado objeto al mismo tiempo. El mecanismo de bloqueo permite que múltiples subprocesos accedan de forma segura a recursos críticos.
El código de sincronización está escrito de la siguiente manera:
Código 1:
Objeto obj = nuevo Objeto(); ... sincronizado(obj) { //TODO: Acceder a recursos críticos} El subproceso múltiple de JAVA siempre está lleno de trampas. Si utilizamos booleano como objeto sincronizado, pueden ocurrir las dos situaciones siguientes:
1. Se cree que un objeto está bloqueado, pero en realidad diferentes objetos están sincronizados.
Código 2:
private volatile Boolean isTrue = false; publich void aMethod() { ... sincronizado(isTrue) { isTrue = !isTrue //TODO: Acceder a recursos críticos isTrue = !isTrue; A primera vista, el código anterior no tiene nada de malo. Debido al uso de sincronizado (isTrue), solo un subproceso puede acceder a recursos críticos al mismo tiempo, pero este no es el caso. Porque las dos constantes falsa y verdadera corresponden a dos objetos diferentes. Cuando isTrue cambia, es probable que diferentes subprocesos sincronicen diferentes objetos. El boxeo automático de JAVA cambiará falso a Boolean.FALSE y verdadero a Boolean.TRUE (esto también muestra que si false se cambia a Boolean.FALSE, el resultado será el mismo). Escriba el código de prueba para una de las situaciones anteriores de la siguiente manera:
Código 3:
public class BooleanTest { private volatile Boolean isTrue = Boolean.FALSE; //Es lo mismo que false aquí public void aMethod() { for(int i=0;i<10;i++) { Thread t = new Thread() { public void run() { sincronizado(esVerdadero) { esVerdadero = !Es Verdadero System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue); try{ Doble ejecución = 1000 * Math.random(); Thread.sleep(ran.intValue()); }catch(InterruptedException e) {} if(!isTrue) System.out.println( Thread.currentThread().getName() + " - ¡Oh, no!"); esTrue = !isTrue; t.start(); } } public static void main(String... args) { BooleanTest bt = new BooleanTest(); Cuando ejecute el código anterior, verá "-¡Oh, no!" de vez en cuando, lo que indica que diferentes subprocesos ingresan al bloque de código sincronizado al mismo tiempo.
2. Se piensa que diferentes objetos están sincronizados, pero en realidad es un objeto.
A veces es posible que deseemos sincronizar varios objetos. Si se utiliza booleano como objeto sincronizado, es probable que dos bloques de sincronización que no deberían tener relación utilicen el mismo bloqueo de objeto. Los ejemplos son los siguientes:
Código 4:
private volatile Boolean aBoolean = Boolean.FALSE; private volatile Boolean anotherBoolean = false; public void aMethod() { ... sincronizado(aBoolean) { //TODO: Acceder al recurso crítico 1 } ... } public void anotherMethod() { . .. sincronizado(anotherBoolean) { //TODO: Acceder al recurso crítico 2 } ... } Supongamos que originalmente dos conjuntos de subprocesos no relacionados llamarán a un método y a otro método. Sin embargo, dado que Boolean.FALSE y false apuntan al mismo objeto, el recurso crítico 1 puede bloquear el acceso al recurso crítico 2 (y viceversa).
Las dos situaciones anteriores indican que cuando use bloques sincronizados, intente no usar objetos booleanos como objetos sincronizados; de lo contrario, pueden ocurrir problemas inesperados o pueden causar trampas en futuras modificaciones del código.
De esto también se desprende que cualquier sincronización de constantes es arriesgada. Si debe sincronizar valores booleanos, debe utilizar el nuevo operador para crear un objeto booleano.