Duplo verificado bloqueio em singleton

aqui está minha class personalizada para o padrão singleton. neste código, eu uso o bloqueio verificado como abaixo. Como eu leio muitos posts em alguma fonte, eles dizem que checagem dupla é útil porque evita que dois threads simultâneos executados ao mesmo tempo façam dois objects diferentes.

public class DoubleCheckLocking { public static class SearchBox { private static volatile SearchBox searchBox; // private constructor private SearchBox() {} // static method to get instance public static SearchBox getInstance() { if (searchBox == null) { // first time lock synchronized (SearchBox.class) { if (searchBox == null) { // second time lock searchBox = new SearchBox(); } } } return searchBox; } } 

Eu ainda não entendo muito o código acima. Qual é o problema, se dois segmentos juntos executarem a mesma linha de código quando a instância for nula?

 if (searchBox == null) { synchronized (SearchBox.class) { if (searchBox == null) { searchBox = new SearchBox(); } } } 

Quando isso aparecer. ambos os dois segmentos verão object é nulo. então ambos sincronizam. e, em seguida, eles verificam novamente e ainda vêem nulo . e crie dois objects diferentes. OOOPS.

Por favor, explique para mim. O que eu entendi errado?

Obrigado 🙂

Não, desde que você está obtendo o bloqueio no SearchBox.class , apenas um thread entrará no bloco sincronizado de cada vez. Então, o primeiro segmento entra, em seguida, encontra searchBox e cria e, em seguida, deixa o bloco sincronizado, em seguida, o segundo segmento digite o bloco, em seguida, ele descobre que o searchBox não é nulo porque o primeiro segmento já o criou para não criar uma nova instância de searchBox

O padrão duplo verificado é usado para evitar a obtenção do bloqueio toda vez que o código for executado, se a chamada não estiver ocorrendo em conjunto, a primeira condição falhará e a execução do código não executará o bloqueio, economizando resources.

Vamos ver este código:

 1 if (searchBox == null) { 2 synchronized (SearchBox.class) { 3 if (searchBox == null) { 4 searchBox = new SearchBox(); 5 } 6 } 

Vamos tentar raciocinar sobre isso. Digamos que temos dois threads A e B e vamos supor que pelo menos um deles atinja a linha 3 e observe que searchBox == null é true . Dois segmentos não podem estar ambos na linha 3 ao mesmo tempo devido ao bloco synchronized . Essa é a chave para entender por que o bloqueio duplo verificado funciona. Então, deve ser o caso de que A ou B feito isso primeiro synchronized . Sem perda de generalidade, digamos que esse segmento é A Então, ao ver searchBox == null é true, ele irá inserir o corpo da instrução e definir searchBox para uma nova instância de SearchBox . Então, eventualmente, sairá do bloco synchronized . Agora será a vez de B entrar: lembre-se, B foi bloqueado esperando A sair. Agora, quando entrar no bloco, ele observará o searchBox . Mas A terá deixado apenas o searchBox para um valor não null . Feito.

A propósito, em Java, a melhor maneira de implementar um singleton é usar um tipo de enum elemento único. De Java eficaz :

Embora essa abordagem ainda não tenha sido amplamente adotada, um tipo de enumeração de elemento único é a melhor maneira de implementar um singleton.

Esse bloqueio de verificação dupla é necessário apenas se você estiver preocupado com muitos encadeamentos que chamam o singleton simultaneamente ou com o custo de obter um bloqueio em geral.

Sua finalidade é evitar a synchronization desnecessária, mantendo assim seu código rápido em um ambiente multi-threaded.

Confira este link para mais informações.

Se você estiver executando no Java 1.5 ou superior, e usar a palavra-chave volatile no mecanismo bloqueado de verificação dupla, ela funcionará bem. Como você está usando a palavra-chave volatile , seu exemplo não é quebrado de acordo com o mesmo link acima.

 if (searchBox == null) { //1 synchronized (SearchBox.class) { if (searchBox == null) { //2 searchBox = new SearchBox(); } } } } 
  1. Se uma instância já foi criada, não faça nada – evite bloquear encadeamentos
  2. O primeiro thread que adquiriu o bloqueio verifica e vê que não existe tal object e o cria. Ele libera o bloqueio e o segundo pode fazer o mesmo – ele precisa verificar se o object existe porque o primeiro pode tê-lo criado.

Então, basicamente, o outer if é usado para evitar bloqueios redundantes – ele permite que todos os threads saibam que já existe um object e eles não precisam bloquear / fazer nada. E o if interno é usado para permitir que um thread concorrente saiba se outro já criou o object ou não.