Double Checked Locking in Singleton

ecco la mia class personalizzata per il modello singleton. in questo codice, utilizzo il blocco a doppio controllo come di seguito. Mentre leggo molti post su una fonte, dicono che il double check è utile perché impedisce a due thread simultanei di eseguire contemporaneamente due oggetti diversi.

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; } } 

Non riesco ancora a capire il codice sopra così tanto. Qual è il problema, se due thread insieme eseguono la stessa riga di codice quando l’istanza è nullo?

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

Quando appare. entrambi i thread vedranno l’object nullo. quindi entrambi si sincronizzano. e poi, controllano di nuovo, e continuano a vederlo nullo . e creare due oggetti diversi. OOOPS.

Per favore, spiegami per me. Cosa ho capito di sbagliato?

Grazie 🙂

No, dal momento che stai ottenendo il blocco su SearchBox.class , solo un thread entrerà nel blocco sincronizzato alla volta. Quindi il primo thread entra quindi trova searchBox e lo crea e poi lascia il blocco sincronizzato, quindi il secondo thread entra nel blocco, quindi trova che searchBox non è nullo perché il primo thread lo ha già creato in modo da non creare una nuova istanza di searchBox

Il pattern Double checked viene utilizzato per evitare di ottenere il lock ogni volta che il codice viene eseguito, se la chiamata non avviene contemporaneamente, la prima condizione fallirà e l’esecuzione del codice non eseguirà il locking risparmiando così risorse.

Diamo un’occhiata a questo codice:

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

Proviamo a ragionare su questo. Diciamo che abbiamo due thread A e B e supponiamo che almeno uno di loro raggiunga la linea 3 e osserva searchBox == null è true . Due fili non possono essere entrambi alla linea 3 contemporaneamente a causa del blocco synchronized . Questa è la chiave per capire perché funziona il blocco con doppia verifica. Quindi, deve essere il caso che A o B siano synchronized prima synchronized . Senza perdita di generalità, dì che quel thread è A Quindi, dopo aver visto searchBox == null è true, entrerà nel corpo searchBox e imposta searchBox in una nuova istanza di SearchBox . Alla fine uscirà dal blocco synchronized . Ora sarà il turno di B per entrare: ricorda, B stato bloccato in attesa di A per uscire. Ora quando entra nel blocco, osserverà searchBox . Ma A avrà lasciato solo aver impostato searchBox su un valore non null . Fatto.

Tra l’altro, in Java, il modo migliore per attuare un singoletto è quello di utilizzare un singolo elemento enum tipo. Da efficace Java :

Sebbene questo approccio debba ancora essere ampiamente adottato, un tipo di enum a elemento singolo è il modo migliore per implementare un singleton.

Questo doppio blocco di controllo è necessario solo se si è preoccupati di molti thread che chiamano simultaneamente il singleton o il costo di ottenere un blocco in generale.

Il suo scopo è quello di prevenire la sincronizzazione inutili, mantenendo in tal modo il codice velocemente in un ambiente multi-threaded.

Dai un’occhiata a questo link per maggiori informazioni.

Se si esegue in Java 1.5 o versioni successive e si utilizza la parola chiave volatile nel meccanismo di blocco doppio controllo, funzionerà correttamente. Poiché stai utilizzando la parola chiave volatile , il tuo esempio non viene interrotto in base allo stesso link sopra.

 if (searchBox == null) { //1 synchronized (SearchBox.class) { if (searchBox == null) { //2 searchBox = new SearchBox(); } } } } 
  1. Se un’istanza è già stata creata, non fare nulla, evita di bloccare i thread
  2. Il primo thread che ha acquisito il blocco controlla e vede che non esiste tale object e lo crea. Rilascia il blocco e il secondo può fare lo stesso: deve controllare se l’object esiste perché il primo potrebbe averlo creato.

Quindi, in sostanza, l’esterno if è usato per prevenire i blocchi ridondanti – consente a tutti i thread di sapere che esiste già un object e che non hanno bisogno di bloccare / fare nulla. E l’interno if viene utilizzato per lasciare un thread concorrente sapere se un altro ha già creato l’object o meno.