A simultaneidade multithread pode ser alcançada em JAVA por meio da instrução sincronizada. Usando blocos de código sincronizados, a JVM garante que apenas um thread possa manter o bloqueio em um determinado objeto ao mesmo tempo. O mecanismo de bloqueio permite que vários threads acessem recursos críticos com segurança.
O código de sincronização é escrito da seguinte forma:
Código 1:
Object obj = new Object(); ... sincronizado(obj) { //TODO: acessar recursos críticos} O multithreading do JAVA está sempre cheio de armadilhas. Se usarmos Boolean como objeto sincronizado, as duas situações a seguir podem ocorrer:
1. Pensa-se que um objeto está bloqueado, mas objetos diferentes estão, na verdade, sincronizados.
Código 2:
private volátil Boolean isTrue = false; publich void aMethod() { ... sincronizado(isTrue) { isTrue = !isTrue; À primeira vista, não há nada de errado com o código acima, pois sincronizado(isTrue) é usado, apenas um thread pode acessar recursos críticos ao mesmo tempo, mas este não é o caso. Porque as duas constantes falsa e verdadeira correspondem a dois objetos diferentes. Quando isTrue muda, é provável que diferentes threads sincronizem objetos diferentes. O boxe automático do JAVA mudará false para Boolean.FALSE e true para Boolean.TRUE (isso também mostra que se false for alterado para Boolean.FALSE, o resultado será o mesmo). Escreva o código de teste para uma das situações acima da seguinte maneira:
Código 3:
public class BooleanTest { private volátil Boolean isTrue = Boolean.FALSE; //É o mesmo se você usar false aqui public void aMethod() { for(int i=0;i<10;i++) { Thread t = new Thread() { public void run() { sincronizado (isTrue) { isTrue = !isTrue; System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue); try{ Double ran = 1000 * Math.random(); Thread.sleep(ran.intValue()); }catch(InterruptedException e) {} if(!isTrue) System.out.println( Thread.currentThread().getName() + " - Ah, não!"); t.start(); } } public static void main(String... args) { BooleanTest bt = new BooleanTest(); Ao executar o código acima, você verá "-Oh, não!" de vez em quando, indicando que diferentes threads entram no bloco de código sincronizado ao mesmo tempo.
2. Pensa-se que diferentes objetos estão sincronizados, mas na verdade é um objeto.
Às vezes, podemos querer sincronizar vários objetos. Se Boolean for usado como objeto sincronizado, é provável que dois blocos de sincronização que não deveriam ter relacionamento usem o mesmo bloqueio de objeto. Os exemplos são os seguintes:
Código 4:
private volátil Boolean aBoolean = Boolean.FALSE; private volátil Boolean anotherBoolean = false; public void aMethod() { ... sincronizado(aBoolean) { //TODO: Acessar recurso crítico 1 } ... } public void anotherMethod() { . .. sincronizado(anotherBoolean) { //TODO: acessar recurso crítico 2 } ... } Suponha que originalmente aMethod e anotherMethod serão chamados por dois conjuntos de threads não relacionados. Porém, como Boolean.FALSE e false apontam para o mesmo objeto, o acesso ao recurso crítico 2 pode ser bloqueado pelo recurso crítico 1 (e vice-versa).
As duas situações acima indicam que ao usar blocos sincronizados, tente não usar objetos booleanos como objetos sincronizados, caso contrário podem ocorrer problemas inesperados ou traps podem ser causados em futuras modificações de código.
Também pode ser visto que qualquer sincronização de constantes é arriscada. Se você precisar sincronizar o booleano, deverá usar o operador new para criar um objeto booleano.