Domanda

Stavo leggendo questo articolo su " Double -Controllo bloccato " e dal tema principale dell'articolo mi chiedevo perché a un certo punto dell'articolo l'autore usasse il prossimo linguaggio:

  

Listato 7. Tentativo di risolvere il problema di scrittura fuori servizio

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

E la mia domanda è: C'è qualche motivo per sincronizzare due volte un po 'di codice con lo stesso blocco? Hai qualche scopo?

Molte grazie in anticipo.

È stato utile?

Soluzione

Il punto di blocco due volte era di tentare di impedire scritture non ordinate. Il modello di memoria specifica dove possono verificarsi i riordini, in parte in termini di blocchi. Il blocco assicura che nessuna scrittura (inclusa alcuna all'interno del costruttore singleton) appaia dopo che " instance = inst; " linea.

Tuttavia, per approfondire l'argomento, raccomanderei L'articolo di Bill Pugh . E poi non provarci mai :)

Altri suggerimenti

L'articolo fa riferimento al modello di memoria Java (JMM) precedente alla 5.0. Sotto quel modello lasciando un blocco sincronizzato forzato scrive nella memoria principale. Quindi sembra essere un tentativo di assicurarsi che l'oggetto Singleton venga espulso prima del riferimento ad esso. Tuttavia, non funziona del tutto perché la scrittura sull'istanza può essere spostata nel blocco - il motel del triotto.

Tuttavia, il modello precedente alla 5.0 non è mai stato implementato correttamente. 1.4 dovrebbe seguire il modello 5.0. Le lezioni sono iniziate pigramente, quindi potresti anche scrivere

public static final Singleton instance = new Singleton();

O meglio, non usare i singoli perché sono cattivi.

Jon Skeet ha ragione: leggi Bill Pugh's articolo. Il linguaggio usato da Hans è la forma precisa che non funzionerà e non dovrebbe essere usato.

Non è sicuro:

private static Singleton instance;

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

Anche questo non è sicuro:

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

Non fare nessuno dei due, mai.

Invece, sincronizza l'intero metodo:

    public static synchronized Singleton getInstance() {
      if (instance == null) {
        instance = new Singleton();
      }
      return instance;
    }

A meno che non si stia recuperando questo oggetto un miliardo di volte al secondo il colpo di performance, in termini reali, è trascurabile.

Seguendo la John Skeet raccomandazione:

  

Tuttavia, per approfondire l'argomento   Consiglierei l'articolo di Bill Pugh. E   quindi non tentare mai :)

Ed ecco la chiave per il secondo blocco di sincronizzazione:

  

Questo codice inserisce la costruzione di   Oggetto helper all'interno di un interno   blocco sincronizzato. L'idea intuitiva   ecco che dovrebbe esserci un ricordo   barriera nel punto in cui   la sincronizzazione viene rilasciata e questo   dovrebbe impedire il riordino del file   inizializzazione dell'oggetto Helper   e l'assegnazione al campo   aiutante.

Quindi, fondamentalmente, con il blocco di sincronizzazione interno, stiamo provando a "imbrogliare" il JMM che crea l'istanza all'interno del blocco di sincronizzazione, per forzare il JMM ad eseguire tale allocazione prima del completamento del blocco di sincronizzazione. Ma il problema qui è che il JMM ci sta guidando e sta spostando l'assegnazione che è prima del blocco di sincronizzazione all'interno del blocco di sincronizzazione, riportando il nostro problema all'inizio.

Questo è quello che ho capito da quegli articoli, davvero interessante e ancora una volta grazie per le risposte.

Va ??bene, ma l'articolo lo diceva

  

Il codice nel Listato 7 non funziona a causa della definizione corrente del modello di memoria. Java Language Specification (JLS) richiede che il codice all'interno di un blocco sincronizzato non venga spostato da un blocco sincronizzato. Tuttavia, non dice che il codice che non si trova in un blocco sincronizzato non può essere spostato in un blocco sincronizzato.

E sembra anche che JVM faccia la prossima traduzione in "pseudo-codice" in ASM:

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          //inst = new Singleton();      //4
          instance = new Singleton();               
        }
        //instance = inst;               //5
      }
    }
  }
  return instance;
}

Finora, il punto di non scrittura dopo " instance = inst " non è realizzato?

Leggerò ora l'articolo, grazie per il link.

A partire da Java 5, è possibile far funzionare il doppio controllo dichiarando il campo volatile.

Vedi http://www.cs.umd.edu /~pugh/java/memoryModel/DoubleCheckedLocking.html per una spiegazione completa.

A proposito di questo idioma c'è un articolo molto consigliabile e chiarificatore:

http: // www .javaworld.com / JavaWorld / JW-02-2001 / JW-0209-double.html? page = 1

D'altra parte, penso che cosa significhi dhighwayman.myopenid è il motivo per cui lo scrittore ha inserito un blocco sincronizzato riferito alla stessa classe (sincronizzato (Singleton.class)) all'interno di un altro blocco sincronizzato riferito alla stessa classe. Può accadere quando viene creata una nuova istanza (Singleton inst = instance;) all'interno di quel blocco e per garantire che sia thread-safe è necessario scriverne un'altra sincronizzata.

Altrimenti, non vedo alcun senso.

Guarda Google Tech Talk sul Java Memory Model per una presentazione davvero piacevole ai punti più fini del MMM. Dato che manca qui, vorrei anche segnalare il blog di Jeremy Mansons 'Java Concurrency' esp. il post su Double Checked lock (chiunque sia presente in il mondo Java sembra avere un articolo su questo :).

Per Java 5 e versioni successive esiste in realtà una variante con doppio controllo che può essere migliore della sincronizzazione dell'intero programma di accesso. Questo è anche menzionato nella Dichiarazione di blocco con doppio controllo :

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

La differenza chiave qui è l'uso di volatile nella dichiarazione delle variabili - altrimenti non funziona e comunque non funziona in Java 1.4 o meno.

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