Desta forma, diversos pesquisadores têm estudado e proposto algumas "artimanhas" como técnicas para evitar que o gerenciamento da JMM não efete o algorítimo desejado. Em [1], a construção do objeto a ser criado é colocado em um bloco (extra) synchronized interno. Ou seja, no momento em que a sincronização (interna) é finalizada, deve-se existir uma "barreira de memória", evitando assim que a JMM reordene as instruções para inicialização do objeto. Entretanto, ainda sim é possível que a reordenação aconteça devido a não existência de regras para impedir que a execução de instruções que estão após o bloco sincronizado seja antes de finalizar o "monitor". Uma outra solução, proposta por Goetz [3], utiliza uma variável temporária para garantir a construção total do objeto e posteriormente atribuída à referência original. Porém, ainda sim a JMM está livre para realizar otimizações nesta variável temporária. Goetz ainda propõe outra abordagem, utilizando uma variável booleana cujo valor depende diretamente da existência na memória do objeto a ser criado e posicionada fora do bloco synchronized. Porém, o compilador ou a JVM (Java Virtual Machine) pode reordenar tal instrução e adicioná-la ao bloco sincronizado para reduzir problemas associados a cache. Ou seja, o valor desta variável pode ser atribuída antes da construção do objeto. Em [4], Goetz discute a solução deste problema através da classe ThreadLocal. Em vez de verificar se o objeto possui referência nula, um objeto ThreadLocal (estático) armazena um valor (booleano, por exemplo) para garantir afirmar que uma outra thread já inicializou o objeto desejado. Por não compartilhar este valor com outras threads (pois este valor é local), problemas com reordenação de instruções não efetará a solução. Apesar de alcançar o objeto desejado pelo DCL, a performance da ThreadLocal é considerada pior que o custo existente em blocos sincronizados.
Apesar de existirem soluções plausíveis para alcançar o objetivo do Double-Checked Locking, a maior parte destas não funcionam ou possuem baixa performance/otimização. Acho que em qualquer domínio de aplicações que necessitem de alta performance sempre haverá trade-offs, ficando a cargo dos programadores decidir cuidadosamente quando um código deve ou não ser otimizado e, neste processo, se de fato haverá ganho considerável.
Ainda segundo Goetz, métodos sincronizados podem ser até 100 vezes mais lento do que métodos não-sincronizados. Para colocar em prova tal afirmação,
realizei alguns experimentos utilizando uma máquina com as seguintes configurações:
- Processador Pentium-M, 1.86
- 1GB Memória RAM
Utilizei um método estatístico intitulado de Distribuição Z [2]. Foram realizados 10 experimentos para cada tipo de método (sincronizado e não sincronizado), e alcancei os seguintes resultados com médias para 95% de nível de confiança:
- Métodos sincronizados: 32071.90 milissegundos. Intervalo de confiança: entre 31743.89 e 32399.91 milissegundos.
- Métodos não-sincronizados: 9691.00 milissegundos. Intervalo de confiança: entre 9419.32 e 9962.68 milissegundos.
Thiago Sales
[1] David Bacon, Joshua Bloch, Jeff Bogda, and Cliff Click. The ”double-checked locking is broken”declaration. http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html, Fevereiro 2001. Último acesso em 2 de Maio de 2008.
[2] Fisher. On a distribution yielding the error functions of several well known
statistics. Proceedings of the International Congress of Mathematics, 2:805–
813, 1924
[3] Brian Goetz. Can double-checked locking be fixed?
http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html, Maio 2001. Último acesso em 2 de Maio de 2008.
[4] Brian Goetz. Can threadlocal solve the double-checked locking problem?
http://www.javaworld.com/javaworld/jw-11-2001/jw-1116-dcl.html,
Novembro 2001. Último acesso em 2 de Maio de 2008.
[5] Brian Goetz. Double-checked locking: Clever, but broken.
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html, Fevereiro 2001. Último acesso em 2 de Maio de 2008.
Nenhum comentário:
Postar um comentário