Domanda

Sto guardando un po 'di codice nella nostra app che penso possa incontrare un caso di "Blocco a doppio controllo"Ho scritto un codice di esempio simile a quello che facciamo.

Qualcuno può vedere come questo può sperimentare il blocco a doppio controllo? O è sicuro?

class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        Helper result;
        synchronized(this) {
            result = helper;
        }

        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }
}

Codice base preso in prestito da wiki.

È stato utile?

Soluzione

L'intero punto del blocco a doppio controllo è che il percorso veloce (quando non è necessario istanziare l'oggetto) non è sincronizzato. Quindi quello che hai non è il blocco a doppio controllo.

È necessario sbarazzarti del primo blocco sincronizzato per ottenere un blocco a doppio controllo rotto. Allora devi fare helper volatile per risolverlo.

Altri suggerimenti

È inutilmente complesso, il modo più semplice "sicuro" per fare DCL è così:

class Foo {
  private volatile Helper helper = null;
  private final Object mutex = new Object(); 
  public Helper getHelper() {
    if (helper == null) {
        synchronized(mutex) {
            if (helper == null) {
                helper = new Helper();
            }
        }
    }
    return helper;
  }
}

I punti chiave qui sono:

  • Nel caso "felice" ci aspettiamo che Helper venga già assegnato, quindi se lo è possiamo semplicemente restituirlo senza dover inserire un blocco sincronizzato.
  • Helper è contrassegnato come volatile per far sapere al compilatore che l'helper può essere letto da / scritto da qualsiasi thread in qualsiasi momento ed è importante che le letture / le scritture non vengano riordinate.
  • Il blocco sincronizzato utilizza una variabile finale privata su cui sincronizzarsi per evitare un potenziale colpo di prestazioni nel caso di un'altra area di sincronizzazione del codice sul this esempio.

Questa parte è un blocco a doppio controllo standard:

if (helper == null) {
    synchronized(this) {
        if (helper == null) {
            helper = new Helper();
        }
    }
}

La prima parte è un incarico inutile che non fa nulla alla parte di bloccaggio a doppio controllo: se Helper è nullo, allora viene eseguito comunque, se non lo è non sarebbe comunque eseguito. È completamente inefficace.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top