Pergunta

Vi alguns exemplos em java onde eles fazem sincronização em um bloco de código para alterar alguma variável enquanto essa variável foi declarada volátil originalmente..Eu vi isso em um exemplo de classe singleton onde eles declararam a instância única como volátil e sincronizaram o bloco que inicializa essa instância ...Minha pergunta é por que declaramos volátil enquanto sincronizamos, por que precisamos fazer as duas coisas?um deles não é suficiente para o outro ??

public class someClass {
volatile static uniqueInstance = null;

public static someClass getInstance() {
        if(uniqueInstance == null) {
            synchronized(someClass.class) {
                if(uniqueInstance == null) {
                    uniqueInstance = new someClass();
                }
            }
        }
        return uniqueInstance;
    }

desde já, obrigado.

Foi útil?

Solução

A sincronização por si só seria suficiente neste caso se a primeira verificação estivesse dentro do bloco sincronizado (mas não é e um thread pode não ver as alterações executadas por outro se a variável não fosse volátil).Volátil por si só não seria suficiente porque é necessário realizar mais de uma operação atomicamente.Mas cuidado!O que temos aqui é o chamado bloqueio duplo verificado - uma expressão comum, que infelizmente não funciona de forma confiável.Acho que isso mudou desde o Java 1.6, mas ainda assim esse tipo de código pode ser arriscado.

EDITAR:quando a variável é volátil, esse código funciona corretamente desde o JDK 5 (não 6 como escrevi anteriormente), mas não funcionará conforme esperado no JDK 1.4 ou anterior.

Outras dicas

Isso usa o bloqueio duplo verificado, observe que o if(uniqueInstance == null) não está dentro da parte sincronizada.

Se uniqueInstance não é volátil, ele pode ser "inicializado" com um objeto parcialmente construído onde partes dele não são visíveis para outros que não sejam o thread em execução no synchronized bloquear.volátil torna esta uma operação de tudo ou nada neste caso.

Se você não tivesse o bloco sincronizado, poderia acabar com 2 threads chegando a esse ponto ao mesmo tempo.

if(uniqueInstance == null) {
      uniqueInstance = new someClass(); <---- here

E você constrói 2 objetos SomeClass, o que vai contra o propósito.

A rigor, você não precisa de volátil, o método poderia ter sido

public static someClass getInstance() {
    synchronized(FullDictionary.class) {
         if(uniqueInstance == null) {
             uniqueInstance = new someClass();
          }
         return uniqueInstance;
    }
}

Mas isso implica na sincronização e serialização de cada thread que executa getInstance().

Esta postagem explica a ideia por trás de volátil.

Também é abordado na obra seminal, Simultaneidade Java na prática.

A ideia principal é que a simultaneidade não envolve apenas a proteção do estado compartilhado, mas também a visibilidade desse estado entre threads:é aqui que entra o volátil.(Este contrato maior é definido pelo Modelo de memória Java.)

Você pode fazer a sincronização sem usar o bloco sincronizado.Não é necessário usar variável volátil nele ...Atualizações voláteis A única variável da memória principal ... e atualização sincronizada todas as variáveis ​​compartilhadas que foram acessadas da memória principal.Então você pode usá-lo de acordo com sua necessidade.

Meus dois centavos aqui

Primeiro, uma rápida explicação da intuição deste código

if(uniqueInstance == null) {
        synchronized(someClass.class) {
            if(uniqueInstance == null) {
                uniqueInstance = new someClass();
            }
        }
    }

A razão pela qual ele verifica uniqueInstance == null duas vezes é para reduzir a sobrecarga de chamada do bloco sincronizado, que é relativamente mais lento.O chamado bloqueio verificado duas vezes.

Em segundo lugar, o motivo pelo qual ele usa sincronizado é fácil de entender, pois torna atômicas as duas operações dentro do bloco sincronizado.

Por fim, o modificador volátil garante que todos os threads vejam a mesma cópia, portanto a primeira verificação fora do bloco sincronizada verá o valor do exclusivoInstance de uma maneira que é "sincronizada" com o bloco sincronizado.Sem o modificador volátil, um thread pode atribuir um valor a uniqueInstance, mas o outro thread pode não vê-lo na primeira verificação.(Embora a segunda verificação o veja)

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