Em que situações pode um bloco sincronizado vazio alcançar semântica de threading corretos?

StackOverflow https://stackoverflow.com/questions/686415

Pergunta

Eu estava olhando através de uma href="http://findbugs.sourceforge.net/" rel="noreferrer"> Findbugs relatório na minha base de código e um dos padrões que foi desencadeada era para um bloco synchronzied vazio (ou seja, synchronized (var) {}). A documentação diz :

blocos sincronizados vazios são muito mais sutil e difícil de usar corretamente do que a maioria das pessoas reconhecem e esvaziar blocos sincronizados quase nunca são um melhor solução do que menos artificial soluções.

No meu caso, ocorreu porque o conteúdo do bloco havia sido comentado, mas a declaração synchronized ainda estava lá. Em que situações pode um bloco synchronized vazio alcançar semântica de threading corretos?

Foi útil?

Solução

Um bloco sincronizado vazio irá esperar até que ninguém mais está usando esse sincronizador. Isso pode ser o que você quer, mas porque você não ter protegido o código subsequente no bloco sincronizado, nada está parando alguém de modificar o que sempre foi que você estava esperando, enquanto você executar o código subseqüente. Isso é quase nunca o que você quer.

Outras dicas

As respostas anteriores deixar de sublinhar a coisa mais útil sobre blocos synchronized vazias: eles podem garantir a visibilidade de mudanças de variáveis ??e outras ações em threads. Como jtahlborn indica, sincronização impõe uma "barreira de memória" no compilador que obriga a descarga, e atualizar seus caches. Mas eu não encontrei onde “Snake discute” isto, então eu escrevi uma resposta a mim mesmo.

int variable;

void test() // This code is INCORRECT
{
    new Thread( () ->  // A
    {
        variable = 9;
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

O programa acima está incorreto. O valor da variável pode ser armazenado em cache localmente no segmento A ou B ou ambos. Então B não pode ler o valor de 9 que A escreve, e pode, portanto, em laço para sempre.

Faça uma mudança variável visível entre threads usando blocos vazios synchronized

Uma possível correção é adicionar um volatile (efetivamente "sem cache") modificador para a variável. Às vezes isso é ineficiente, no entanto, porque proíbe totalmente o cache da variável. blocos synchronized vazios, por outro lado, não proíbem cache. Todos eles fazem é forçar os caches para sincronizar com a memória principal em certos pontos críticos. Por exemplo: *

int variable;

void test() // Corrected version
{
    new Thread( () ->  // A
    {
        variable = 9;
        synchronized( o ) {} // Flush to main memory
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            synchronized( o ) {} // Refresh from main memory
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

final Object o = new Object();

Como a memória garantias modelo visibilidade

Ambos os tópicos deve sincronizar no mesmo objeto, a fim de visibilidade garantia. Esta garantia baseia-se na Java memória modelo , em especial sobre a regra de que uma "ação de desbloqueio no monitor m sincroniza-com todas as ações de bloqueio subsequentes sobre m" e, assim, acontece-antes aqueles ações. Então desbloqueio de A do Monitor de o na cauda do seu bloco synchronized acontece-antes bloqueio posterior do B à frente de seu bloco. (Nota, é esta ordem cauda-cabeça estranho da relação que explica por que os corpos podem estar vazio.) Além disso, dado que a gravação de A precede o seu desbloqueio e bloqueio de B precede a sua leitura, a relação deve se estender para cobrir ambos gravação e leitura: < em> gravação acontece-antes leitura . É esta relação fundamental, estendida que faz com que o programa revisto correto em termos do modelo de memória.

Eu acho que isso é o uso mais importante para blocos synchronized vazias.


* Eu falo como se fosse uma questão de cache do processador porque eu acho que é uma maneira útil de ver isso. Na verdade, como Aleksandr Dubinsky comentou, ‘todos os processadores modernos são cache-coerente. O acontece-antes de relacionamento é mais sobre o que o compilador é permitido fazer ao invés da CPU.’

Ela costumava ser o caso que a especificação implícita certas operações de barreira de memória ocorreu. No entanto, a especificação agora mudou ea especificação original nunca foi implementado corretamente. Ele pode ser usado para esperar por outro segmento para liberar o bloqueio, mas de coordenação que o outro segmento já adquiriu o bloqueio seria complicado.

A sincronização faz um pouco mais do que apenas esperando, enquanto deselegante codificação isso poderia alcançar o efeito desejado.

A partir http://www.javaperformancetuning.com/news/qotm030.shtml

  1. A rosca adquire o bloqueio no monitor por objecto este (assumindo que o monitor está desbloqueado, caso contrário, as esperas de rosca até que o monitor é desbloqueado).
  2. A memória fio libera todas as suas variáveis, ou seja, ele tem todas as suas variáveis ??efetivamente lidos do "principal" de memória (JVMs pode usar conjuntos sujos para otimizar isso para que apenas as variáveis ??"sujas" são liberadas, mas conceitualmente esta é a mesma . Consulte a seção 17.9 da especificação da linguagem Java).
  3. O bloco de código é executada (neste definindo o valor de retorno para o valor atual do i3, que pode ter sido apenas de reset da memória "main" caso).
  4. (Quaisquer alterações a variáveis ??normalmente agora ser escrita para a memória "main", mas para GETI3 () não temos mudanças.)
  5. O segmento libera o bloqueio no monitor por objeto isso.

Para um olhar em profundidade no modelo de memória do Java, ter um olhar para este vídeo do Google de 'Tópicos avançados em linguagens de programação' série: http://www.youtube.com/watch?v=1FX4zco0ziY

Ela dá um muito bom resumo do que a lata compilador (muitas vezes em teoria, mas às vezes na prática) fazer para o seu código. material essencial para qualquer programador Java sério!

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