Pergunta

Checkstyle relata esse código como "O idioma verifiquei bloqueio está quebrado", mas eu não acho que meu código realmente é afetado pelos problemas com bloqueio duplo verificado.

O código é suposto para criar uma linha em um banco de dados se uma linha com esse ID não existe. Ele é executado em um ambiente multi-threaded e eu quero evitar o-chave primária existe SQL-exceções.

O pseudo-código:

private void createRow(int id) {
  Row row = dao().fetch(id);
  if (row == null) {
     synchronized (TestClass.class) {
        row = dao().fetch(id);
        if (row == null) {
           dao().create(id);
        }
     }
  }
}

posso concordar que ele se parece com bloqueio duplo verificado, mas eu não estou usando variáveis ??estáticas eo código em fetch () e criar () é provavelmente demasiado complexo para ser embutido e colocar fora de ordem.

Am I errado ou checkstyle? :)

Foi útil?

Solução

Assumindo que você deseja que a linha mais interna para ler:

row = dao().create(id);

Não é um problema de bloqueio com verificação dupla clássica assumindo dao().fetch está devidamente mutexed do método de criação.

Editar : (código foi atualizado)

O problema clássico de um bloqueio duplo verificado é ter um valor atribuído antes da inicialização ocorre quando dois segmentos estão acessando o mesmo valor.

Assumindo que o DAO está devidamente sincronizado e não retornará um valor parcialmente inicializado, esta não sofre as falhas do idioma bloqueio duplo verificado.

Outras dicas

Eu acho que neste caso, checkstyle está correto. Em seu código, tal como apresentado, considere o que aconteceria se dois tópicos ambos tinham row == null na entrada para o bloco sincronizado. Thread A entraria no bloco, e inserir a nova linha. Então, depois de rosca Um sai do bloco, linha B entraria no bloco (porque ele não sabe o que aconteceu), e tentar inserir o mesmo nova linha novamente.

Eu vejo você acabou de alterar o código e acrescentou uma linha faltando muito importante lá. Na new código, você pode ser capaz de fugir com que, uma vez que dois tópicos não vai estar contando com alterações em uma variável compartilhada (estático). Mas você pode ser melhor fora de ver se o seu DBMS suporta uma declaração como INSERT OR UPDATE.

Outra boa razão para delegar essa funcionalidade para o SGBD é se você precisar implantar mais de um servidor de aplicativos. Desde blocos synchronized não funcionam entre diferentes máquinas, você terá que fazer outra coisa, nesse caso, de qualquer maneira.

Se você está tentado a escrever código como este, considere:

  • Desde Java 1.4, sincronizando métodos tornou-se muito barato. Não é gratuito, mas o tempo de execução realmente não sofre tanto assim que vale a pena a corrupção de dados de risco.

  • Desde Java 1.5, você tem as Atomic * classes que permitem que você leia e campos definidos de uma maneira atômica. Infelizmente, eles não resolver o seu problema. Por que eles não adicionar AtomicCachedReference ou algo assim (o que chamaria de um método substituível quando get () é chamado e o valor atual == null) está além de mim.

  • Tente ehcache . Ele permite que você configurar um cache (ou seja, e objeto que permite que você chamar o código se a chave é não contido em um mapa). Este é geralmente o que você quer e os caches realmente resolver o seu problema (e todos os outros problemas que você não sabia que ainda existia).

Como os outros têm para fora pontas, este código vai fazer o que você pretende como é, mas somente sob um rigoroso conjunto de premissas não-óbvias:

  1. O código Java é não agrupado (ver resposta de @ Greg H)
  2. A referência "linha" é única que está sendo verificado para nulo na primeira linha, antes do bloco de sincronização.

A razão do idioma bloqueio com verificação dupla está quebrado (por seção 16.2.4 da Java Concurrency in Practice ) é que é possível para um segmento em execução este método para ver um não-nulo, mas indevidamente inicializado referência a "linha", antes de entrar no bloco sincronizado (a menos que "dao" fornece sincronização adequada). Se o seu método estavam fazendo qualquer coisa com "linha" que não seja a verificação de que é nulo ou não, seria quebrado. Tal como está, é provavelmente bem, mas muito frágil - pessoalmente eu não seria confortável cometer este código se eu achava que havia mesmo uma possibilidade remota de que algum outro desenvolvedor em algum momento posterior pode modificar o método sem compreender as sutilezas da DCL.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top