Qual è un modo efficiente per implementare un modello singleton in Java? [chiuso]


812

Qual è un modo efficiente per implementare un modello singleton in Java?


1
"Qual è un modo efficiente per implementare un modello singleton in Java?" si prega di definire efficiente.
Marian Paździoch,

medium.com/@kevalpatel2106/… . Questo è l'articolo completo su come ottenere la sicurezza di thread, riflessione e serializzazione nel modello singleton. Questa è la buona fonte per comprendere i vantaggi e i limiti della classe singleton.
Keval Patel,

Come sottolinea Joshua Bloch in Effective Java, enum singleton è il modo migliore di procedere. Qui ho classificato le varie implementazioni come pigre / desiderose ecc.
isaolmez

Risposte:


782

Usa un enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch ha spiegato questo approccio nel suo discorso efficace su Java Reloaded a Google I / O 2008: collegamento al video . Vedi anche le diapositive 30-32 della sua presentazione ( efficacia_java_reloaded.pdf ):

Il modo giusto di implementare un singleton serializzabile

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Modifica: una parte online di "Efficace Java" dice:

"Questo approccio è funzionalmente equivalente all'approccio di campo pubblico, tranne per il fatto che è più conciso, fornisce gratuitamente i macchinari di serializzazione e fornisce una garanzia coraggiosa contro l'istanza multipla, anche di fronte a sofisticati attacchi di serializzazione o di riflessione. Mentre questo approccio ha ancora ampiamente adottato, un tipo enum a elemento singolo è il modo migliore per implementare un singleton . "


203
Penso che le persone dovrebbero iniziare a guardare gli enum come solo una classe con una caratteristica. se puoi elencare le istanze della tua classe in fase di compilazione, usa un enum.
Amir Arad,

7
Personalmente non trovo spesso la necessità di utilizzare direttamente il modello singleton. A volte uso l'iniezione di dipendenza di Spring con un contesto applicativo che contiene quelli a cui si riferisce come singoli. Le mie classi di utilità tendono a contenere solo metodi statici e non ho bisogno di alcuna istanza di essi.
Stephen Denne,

3
Ciao, qualcuno può dirmi come questo tipo di singleton può essere deriso e testato in casi di test. Ho provato a scambiare un'istanza singleton falsa per questo tipo ma non ci sono riuscita.
Ashish Sharma,

29
Immagino abbia senso, ma ancora non mi piace. Come creeresti un singleton che estende un'altra classe? Se usi un enum, non puoi.
Chharvey,

11
@bvdb: se vuoi molta flessibilità, hai già rovinato l'implementazione di un singleton in primo luogo. La capacità di creare un'istanza indipendente quando ne hai bisogno è piuttosto preziosa in sé.
cHao,

233

A seconda dell'uso, ci sono diverse risposte "corrette".

Da java5 il modo migliore per farlo è usare un enum:

public enum Foo {
   INSTANCE;
}

Pre java5, il caso più semplice è:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Esaminiamo il codice. Innanzitutto, vuoi che la lezione sia definitiva. In questo caso, ho usato la finalparola chiave per far sapere agli utenti che è definitiva. Quindi è necessario rendere privato il costruttore per impedire agli utenti di creare il proprio Foo. Lanciare un'eccezione dal costruttore impedisce agli utenti di usare la riflessione per creare un secondo Foo. Quindi si crea un private static final Foocampo per contenere l'unica istanza e un public static Foo getInstance()metodo per restituirlo. La specifica Java garantisce che il costruttore venga chiamato solo quando la classe viene utilizzata per la prima volta.

Quando hai un oggetto molto grande o un codice di costruzione pesante E hai anche altri metodi o campi statici accessibili che potrebbero essere usati prima che sia necessaria un'istanza, allora e solo allora devi usare l'inizializzazione lazy.

È possibile utilizzare a private static classper caricare l'istanza. Il codice sarebbe quindi simile a:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Poiché la linea private static final Foo INSTANCE = new Foo();viene eseguita solo quando viene effettivamente utilizzata la classe FooLoader, questo si occupa dell'istanza pigra ed è garantito che sia sicuro per i thread.

Quando vuoi anche essere in grado di serializzare il tuo oggetto devi assicurarti che la deserializzazione non crei una copia.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Il metodo readResolve()assicurerà che venga restituita l'unica istanza, anche quando l'oggetto è stato serializzato in una precedente esecuzione del programma.


32
Il controllo per la riflessione è inutile. Se un altro codice utilizza la riflessione sui privati, è Game Over. Non c'è motivo di provare a funzionare correttamente anche sotto tale uso improprio. E se ci provi, sarà comunque una "protezione" incompleta, solo un sacco di codice sprecato.
Wouter Coekaerts,

5
> "Per prima cosa, vuoi che la classe sia definitiva". Qualcuno potrebbe approfondire questo per favore?
PlagueHammer,

2
La protezione dalla deserializzazione è completamente rotta (penso che questo sia menzionato in Effective Java 2nd Ed).
Tom Hawtin - tackline

9
-1 questo non è assolutamente il caso più semplice, è inventato e inutilmente complesso. Guarda la risposta di Jonathan per la soluzione più semplice che sia sufficiente nel 99,9% di tutti i casi.
Michael Borgwardt,

2
Questo è utile quando il tuo singleton deve ereditare da una superclasse. In questo caso non è possibile utilizzare il modello enum singleton, poiché enums non può avere una superclasse (tuttavia possono implementare interfacce). Ad esempio, Google Guava utilizza un campo finale statico quando il modello enum singleton non è un'opzione: code.google.com/p/guava-libraries/source/browse/trunk/guava/src/…
Etienne Neveu

139

Disclaimer: ho appena sintetizzato tutte le risposte fantastiche e l'ho scritto con le mie parole.


Durante l'implementazione di Singleton abbiamo 2 opzioni
1. Caricamento lento
2. Caricamento anticipato

Il caricamento lento aggiunge un po 'di sovraccarico di bit (molti ad essere sinceri) quindi usalo solo quando hai un oggetto molto grande o un codice di costruzione pesante E hai anche altri metodi o campi statici accessibili che potrebbero essere utilizzati prima che sia necessaria un'istanza, quindi e solo allora è necessario utilizzare l'inizializzazione lenta. In caso contrario, scegliere il caricamento anticipato è una buona scelta.

Il modo più semplice per implementare Singleton è

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

Tutto è buono tranne il suo singleton caricato in anticipo. Proviamo singleton caricato pigro

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Fin qui tutto bene, ma il nostro eroe non sopravviverà combattendo da solo con più fili malvagi che vogliono molti esempi del nostro eroe. Quindi, consente di proteggerlo dal multi threading male

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

ma non è abbastanza per proteggere l'eroe, Davvero !!! Questo è il meglio che possiamo / dovremmo fare per aiutare il nostro eroe

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Questo è chiamato "Idioma di blocco a doppio controllo". È facile dimenticare l'affermazione volatile e difficile capire perché sia ​​necessario.
Per dettagli: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Ora siamo sicuri del filo diabolico ma della crudele serializzazione? Dobbiamo assicurarci che anche durante la deserializzazione non venga creato alcun nuovo oggetto

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

Il metodo readResolve()assicurerà che venga restituita l'unica istanza, anche quando l'oggetto è stato serializzato in una precedente esecuzione del nostro programma.

Finalmente abbiamo aggiunto una protezione sufficiente contro i thread e la serializzazione, ma il nostro codice sembra voluminoso e brutto. Diamo un cambio al nostro eroe

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Sì, questo è il nostro stesso eroe :)
Dato che la linea private static final Foo INSTANCE = new Foo();viene eseguita solo quando la classe FooLoaderviene effettivamente utilizzata, questo si occupa dell'istanza pigra,

ed è garantito per essere thread-safe.

E siamo arrivati ​​così lontano, ecco il modo migliore per ottenere tutto ciò che abbiamo fatto è il modo migliore possibile

 public enum Foo {
       INSTANCE;
   }

Quale internamente sarà trattato come

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Questo è tutto! Niente più paura della serializzazione, dei thread e del brutto codice. Anche ENUMS singleton sono inizializzati pigramente .

Questo approccio è funzionalmente equivalente all'approccio di campo pubblico, tranne per il fatto che è più conciso, fornisce gratuitamente i macchinari di serializzazione e fornisce una garanzia coraggiosa contro l'istanza multipla, anche di fronte a sofisticati attacchi di serializzazione o di riflessione. Mentre questo approccio deve ancora essere ampiamente adottato, un tipo enum a elemento singolo è il modo migliore per implementare un singleton.

-Joshua Bloch in "Efficace Java"

Ora potresti aver capito perché ENUMS è considerato il modo migliore per implementare Singleton e grazie per la tua pazienza :)
Aggiornato sul mio blog .


3
Solo un chiarimento: i singoli implementati usando enum sono inizializzati pigramente. Dettagli qui: stackoverflow.com/questions/16771373/...

2
Bella risposta. un'ultima cosa, ignorare il metodo clone per generare un'eccezione.
riship89,

2
@xyz belle spiegazioni, mi sono davvero divertito e imparato molto facilmente e spero di non averlo mai dimenticato
Premraj

1
Una delle migliori risposte che abbia mai fatto in rosso su StackOverflow. Grazie!
Shay Tsadok,

1
V'è un problema di serializzazione con l'utilizzo di enumerazioni come Singleton: Tutti i valori di campo sono membro non serializzati e quindi non ripristinate. Vedere Specifica di serializzazione di oggetti Java, versione 6.0 . Un altro problema: nessun controllo delle versioni: tutti i tipi di enum hanno un valore fisso serialVersionUIDdi 0L. Terzo problema: nessuna personalizzazione: qualsiasi metodo writeObject, readObject, readObjectNoData, writeReplace e readResolve specifico per la classe definito dai tipi di enum viene ignorato durante la serializzazione e la deserializzazione.
Basil Bourque,

124

La soluzione pubblicata da Stu Thompson è valida in Java5.0 e successive. Preferirei non usarlo perché penso che sia soggetto a errori.

È facile dimenticare l'affermazione volatile e difficile capire perché sia ​​necessario. Senza la volatile questo codice non sarebbe più sicuro per i thread a causa dell'antipasto a doppio controllo. Per ulteriori informazioni, consultare il paragrafo 16.2.4 della concorrenza Java in pratica . In breve: questo modello (precedente a Java5.0 o senza l'istruzione volatile) potrebbe restituire un riferimento all'oggetto Bar che è (ancora) in uno stato errato.

Questo modello è stato inventato per l'ottimizzazione delle prestazioni. Ma questa non è più una vera preoccupazione. Il seguente codice di inizializzazione lenta è veloce e, soprattutto, più facile da leggere.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

1
Giusto! Sono a mio agio con la volatilità e il suo utilizzo. Oh, e tre saluti per JCiP.
Stu Thompson,

1
Oh, questo è apparentemente l'approccio sostenuto da William Pugh, della fama di FindBugz.
Stu Thompson,

4
@Stu La prima edizione di Effective Java (copyright 2001) descrive questo schema sotto l'articolo 48.
Pascal Thivent,

9
@Bno: che ne dici di rendere privato il costruttore?
xyz,

2
@ AlikElzin-kilaka Non proprio. L'istanza viene creata nella fase di caricamento della classe per BarHolder , che viene ritardata fino alla prima volta in cui è necessaria. Il costruttore di Bar può essere complicato quanto vuoi, ma non verrà chiamato fino al primo getBar(). (E se getBarviene chiamato "troppo presto", allora affronterai lo stesso problema, indipendentemente dal modo in cui i single sono implementati.) Puoi vedere la classe pigra che carica il codice qui sopra: pastebin.com/iq2eayiR
Ti Strga,

95

Thread sicuro in Java 5+:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT : presta attenzione al volatilemodificatore qui. :) È importante perché senza di esso, altri thread non sono garantiti dal JMM (Java Memory Model) per vedere le modifiche al suo valore. La sincronizzazione non se ne occupa: serializza solo l'accesso a quel blocco di codice.

EDIT 2 : La risposta di @Bno descrive in dettaglio l'approccio raccomandato da Bill Pugh (FindBugs) ed è discutibile meglio. Vai a leggere e vota anche la sua risposta.


1
Dove posso saperne di più sul modificatore volatile?
undici81


2
Penso che sia importante menzionare gli attacchi di riflessione. È vero che la maggior parte degli sviluppatori non deve preoccuparsi, ma sembra che esempi come questi (sopra i singleton basati su Enum) dovrebbero includere un codice che protegge dagli attacchi di istanza multipla o semplicemente mettere un disclaimer che indica tali possibilità.
luis.espinal,

2
Qui non sono necessarie parole chiave volatili, poiché la sincronizzazione offre sia l'esclusione reciproca che la visibilità della memoria.
Hemant,

2
Perché preoccuparsi di tutto ciò in Java 5+? La mia comprensione è che l'approccio enum fornisce sia la sicurezza del thread sia l'inizializzazione lazy. È anche molto più semplice ... Inoltre, se vuoi evitare un enum, impedirei comunque l'approccio di classe statica nidificata ...
Alexandros,

91

Dimentica l' inizializzazione lenta , è troppo problematica. Questa è la soluzione più semplice:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

21
la variabile di istanza singleton può anche essere resa definitiva. ad esempio, finale statico privato A singleton = new A ();
jatanp,

19
Questa è effettivamente un'inizializzazione pigra, poiché il singleton statico non verrà istanziato fino a quando la classe non verrà caricata e la classe non verrà caricata fino a quando non sarà necessaria (il che sarà giusto nel momento in cui si fa riferimento per la prima volta al metodo getInstance ()).
Dan Dyer,

7
Se la classe A viene caricata molto prima che si desideri istanziare la statica, è possibile avvolgere la statica in una classe interna statica per disaccoppiare l'inizializzazione della classe.
Tom Hawtin - tackline il

3
sono d'accordo che questa risposta sia la più semplice e Anirudhan, non è necessario dichiarare l'istanza definitiva. Nessun altro thread avrà accesso alla classe mentre i membri statici sono inizializzati. questo è garantito dal compilatore, in altre parole, tutta l'inizializzazione statica viene eseguita in modo sincronizzato - solo un thread.
o

4
Questo approccio ha una limitazione: il costruttore non può generare un'eccezione.
wangf,

47

Assicurati di averne davvero bisogno. Fai un google per "singleton anti-pattern" per vedere alcuni argomenti contrari. Non c'è nulla di intrinsecamente sbagliato in esso, suppongo, ma è solo un meccanismo per esporre alcune risorse / dati globali, quindi assicurati che questo sia il modo migliore. In particolare, ho trovato l'iniezione di dipendenza più utile in particolare se si utilizzano anche test unitari perché DI consente di utilizzare risorse derise a scopo di test.


puoi iniettare valori falsi anche con il metodo tradizionale ma immagino che non sia il modo standard / srping quindi il suo lavoro extra con il solo guadagno è il codice legacy ...
tgkprog

21

Sono sconcertato da alcune delle risposte che suggeriscono DI come alternativa all'uso dei singleton; questi sono concetti non correlati. È possibile utilizzare DI per iniettare istanze singleton o non singleton (ad es. Per thread). Almeno questo è vero se usi Spring 2.x, non posso parlare per altri framework DI.

Quindi la mia risposta all'OP sarebbe (in tutti tranne il codice di esempio più banale) a:

  1. Utilizzare quindi un framework DI come Spring
  2. Renderlo parte della configurazione DI indipendentemente dal fatto che le dipendenze siano singleton, ambito di richiesta, ambito di sessione o altro.

Questo approccio offre una piacevole architettura disaccoppiata (e quindi flessibile e testabile) in cui l'uso di un singleton è un dettaglio dell'implementazione facilmente reversibile (a condizione che tutti i singleton che utilizzi siano thread-safe, ovviamente).


4
Forse perché le persone non sono d'accordo con te. Non ti ho sottovalutato, ma non sono d'accordo: penso che DI possa essere usato per risolvere gli stessi problemi dei singoli. Questo si basa sulla comprensione di "singleton" nel senso di "un oggetto con una singola istanza a cui si accede direttamente con un nome globale", piuttosto che semplicemente "un oggetto con una singola istanza", che è forse leggermente complicato.
Tom Anderson,

2
Per espanderlo leggermente, considera un TicketNumbererelemento che deve avere una singola istanza globale e dove vuoi scrivere una classe TicketIssuerche contiene una riga di codice int ticketNumber = ticketNumberer.nextTicketNumber();. Nel pensiero singleton tradizionale, la precedente riga di codice dovrebbe essere qualcosa di simile TicketNumberer ticketNumberer = TicketNumberer.INSTANCE;. Nel pensiero di DI, la classe avrebbe un costruttore simile public TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }.
Tom Anderson,

2
E diventa qualcun altro il problema di chiamare quel costruttore. Un framework DI lo farebbe con una mappa globale di qualche tipo; un'architettura DI costruita a mano lo farebbe perché il mainmetodo dell'app (o uno dei suoi servitori) creerebbe la dipendenza e quindi chiamerebbe il costruttore. Fondamentalmente, l'uso di una variabile globale (o di un metodo globale) è solo una forma semplice del modello di localizzazione del servizio temuto e può essere sostituito con l'iniezione di dipendenza, proprio come qualsiasi altro uso di quel modello.
Tom Anderson,

@TomAnderson Sono davvero confuso sul perché le persone 'temono' il modello di localizzazione del servizio. Penso che nella maggior parte dei casi sia eccessivo o non necessario nella migliore delle ipotesi, tuttavia, ci sono casi apparentemente utili. Con un numero inferiore di parametri DI è sicuramente preferito, ma immagina 20+. Dire che il codice non è strutturato non è un argomento valido, perché a volte i raggruppamenti di parametri non hanno senso. Inoltre, dal punto di vista del testing delle unità, non mi interessa testare il servizio, solo la sua logica aziendale, e se è codificato correttamente, sarebbe facile. Ho visto questa necessità solo in progetti su larga scala.
Brandon Ling

20

Considera davvero perché hai bisogno di un singleton prima di scriverlo. C'è un dibattito quasi religioso sull'uso di questi che puoi facilmente imbatterti in google singletons in Java.

Personalmente cerco di evitare i singleton il più spesso possibile per molte ragioni, ancora una volta la maggior parte delle quali può essere trovata su google google. Sento che abbastanza spesso i singoli sono abusati perché sono facili da capire da tutti, sono usati come meccanismo per ottenere dati "globali" in un progetto OO e sono usati perché è facile aggirare la gestione del ciclo di vita degli oggetti (o pensando davvero a come puoi fare A dall'interno B). Guarda cose come Inversion of Control (IoC) o Dependency Injection (DI) per un bel mezzo.

Se ne hai davvero bisogno, Wikipedia ha un buon esempio di una corretta implementazione di un singleton.


Concordato. È più di una classe fondamentale che avvia il resto della tua applicazione e se fosse duplicata finirai con un caos completo (cioè accesso singolo a una risorsa o applicazione della sicurezza). Il passaggio di dati globali in tutta l'applicazione è una grande bandiera rossa di accoppiamento. Usalo quando riconosci di averne davvero bisogno.
Salvador Valencia,

16

Di seguito sono riportati 3 diversi approcci

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Doppio controllo Blocco / Caricamento lento

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) Metodo statico di fabbrica

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

13

Uso lo Spring Framework per gestire i miei singoli. Non impone la "singolarità" della classe (cosa che non si può fare comunque se ci sono caricatori di classi multiple) ma fornisce un modo davvero semplice per costruire e configurare fabbriche diverse per la creazione di diversi tipi di oggetti.


11

Wikipedia ha alcuni esempi di singoli, anche in Java. L'implementazione di Java 5 sembra piuttosto completa ed è thread-safe (blocco doppio controllo applicato).


11

Versione 1:

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

Caricamento lento, thread-safe con blocco, basse prestazioni a causa di synchronized.

Versione 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

Caricamento lento, thread threading senza blocco, ad alte prestazioni.


10

Se non hai bisogno di un caricamento lento, prova semplicemente

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Se vuoi un caricamento lento e vuoi che il tuo Singleton sia sicuro per i thread, prova il modello di doppio controllo

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

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

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Poiché non è garantito il funzionamento del modello di doppio controllo (a causa di un problema con i compilatori, non ne so più nulla al riguardo), potresti anche provare a sincronizzare l'intero metodo getInstance o creare un registro per tutti i tuoi Singleton.


2
La prima versione è la migliore. Supponendo che la classe non faccia altro che fornire un singleton, in genere verrà istanziata all'incirca nello stesso punto di quella della seconda versione a causa del caricamento della classe pigra.
Dan Dyer,

1
Il doppio controllo è inutile per una statica. E perché hai reso pubblico il metodo del clone protetto?
Tom Hawtin - tackline il

1
-1 la tua versione del doppio controllo verificato è interrotta.
Assylias,

5
Inoltre devi rendere la tua variabile singletonvolatile
MyTitle

La prima versione è pigra e sicura per i thread.
Miha_x64

9

Direi Enum singleton

Singleton usando enum in Java è generalmente un modo per dichiarare enum singleton. Enum singleton può contenere una variabile di istanza e un metodo di istanza. Per semplicità, si noti inoltre che se si utilizza un metodo di istanza diverso da quello necessario, è necessario garantire la sicurezza del thread di tale metodo se influisce affatto sullo stato dell'oggetto.

L'uso di un enum è molto facile da implementare e non ha inconvenienti per quanto riguarda gli oggetti serializzabili, che devono essere elusi in altri modi.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Puoi accedervi Singleton.INSTANCE, molto più facilmente che chiamare il getInstance()metodo su Singleton.

1.12 Serializzazione delle costanti di Enum

Le costanti di Enum sono serializzate in modo diverso rispetto ai normali oggetti serializzabili o esternalizzabili. La forma serializzata di una costante enum consiste unicamente nel suo nome; i valori dei campi della costante non sono presenti nel modulo. Per serializzare una costante enum, ObjectOutputStreamscrive il valore restituito dal metodo name della costante enum. Per deserializzare una costante enum, ObjectInputStreamlegge il nome costante dallo stream; la costante deserializzata è quindi ottenuta chiamando iljava.lang.Enum.valueOf metodo, passando come argomento il tipo enum della costante e il nome della costante ricevuta. Come altri oggetti serializzabili o esternalizzabili, le costanti enum possono funzionare come target dei riferimenti posteriori che compaiono successivamente nel flusso di serializzazione.

Il processo per cui le costanti enum vengono serializzati non può essere personalizzato: qualsiasi classe-specifica writeObject, readObject, readObjectNoData, writeReplace, e readResolvemetodi definiti dai tipi enum vengono ignorati durante la serializzazione e deserializzazione. Allo stesso modo, anche qualsiasi dichiarazione serialPersistentFieldso serialVersionUIDcampo viene ignorata - tutti i tipi di enum hanno un valore fisso serialVersionUIDdi 0L. Non è necessario documentare campi e dati serializzabili per i tipi di enum, poiché non vi è alcuna variazione nel tipo di dati inviati.

Citato da documenti Oracle

Un altro problema con i Singleton convenzionali è che una volta implementata l' Serializableinterfaccia, non rimangono più Singleton perché il readObject()metodo restituisce sempre una nuova istanza come il costruttore in Java. Questo può essere evitato usando readResolve()e scartando l'istanza appena creata sostituendola con singleton come di seguito

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Questo può diventare ancora più complesso se la tua classe Singleton mantiene lo stato, poiché è necessario renderli transitori, ma con Enum Singleton, la serializzazione è garantita da JVM.


Buona lettura

  1. Modello Singleton
  2. Enum, Singletons e Deserialization
  3. Doppio controllo di blocco e modello Singleton

8
There are 4 ways to create a singleton in java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

(1) non è desideroso, è pigro a causa del meccanismo di caricamento della classe JVM.
Miha_x64

@ Miha_x64 quando ho detto caricamento avido, ho detto inizializzazione desideroso.Se pensi che entrambi siano uguali a ciò che è caricamento desideroso. Forse dovresti scrivere un libro e correggere gli errori commessi da autori precedenti come Joshua Bloch.
Dheeraj Sachan,

L'efficace Java è un ottimo libro, ma richiede sicuramente delle modifiche.
Miha_x64,

@ Miha_x64 cosa sta caricando con impazienza, puoi spiegare con l'esempio
Dheeraj Sachan

Fare qualcosa "avidamente" significa "il più presto possibile". Ad esempio, Hibernate supporta il caricamento delle relazioni con entusiasmo, se richiesto in modo esplicito.
Miha_x64,

8

Potrebbe essere un po 'tardi al gioco su questo, ma ci sono molte sfumature nell'implementazione di un singleton. Il modello di supporto non può essere utilizzato in molte situazioni. E IMO quando si utilizza una volatile, è necessario utilizzare anche una variabile locale. Cominciamo dall'inizio e ripetiamo il problema. Vedrai cosa intendo.


Il primo tentativo potrebbe assomigliare a questo:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Qui abbiamo la classe MySingleton che ha un membro statico privato chiamato INSTANCE e un metodo statico pubblico chiamato getInstance (). La prima volta che viene chiamato getInstance (), il membro INSTANCE è null. Il flusso ricadrà quindi nella condizione di creazione e creerà una nuova istanza della classe MySingleton. Le chiamate successive a getInstance () troveranno che la variabile INSTANCE è già impostata e quindi non creano un'altra istanza di MySingleton. Ciò garantisce che vi sia una sola istanza di MySingleton condivisa tra tutti i chiamanti di getInstance ().

Ma questa implementazione ha un problema. Le applicazioni multi-thread avranno una condizione di competizione sulla creazione della singola istanza. Se più thread di esecuzione colpiscono il metodo getInstance () contemporaneamente (o intorno), ciascuno vedrà il membro INSTANCE come null. Ciò comporterà che ogni thread crei una nuova istanza MySingleton e successivamente imposti il ​​membro INSTANCE.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Qui abbiamo usato la parola chiave sincronizzata nella firma del metodo per sincronizzare il metodo getInstance (). Questo sicuramente risolverà le nostre condizioni di gara. I thread ora bloccano e inseriscono il metodo uno alla volta. Ma crea anche un problema di prestazioni. Questa implementazione non solo sincronizza la creazione della singola istanza, ma sincronizza tutte le chiamate a getInstance (), comprese le letture. Le letture non devono essere sincronizzate in quanto restituiscono semplicemente il valore di ISTANZA. Poiché le letture costituiranno la maggior parte delle nostre chiamate (ricordate, l'istanza si verifica solo alla prima chiamata), incorreremo in un superamento delle prestazioni non necessario sincronizzando l'intero metodo.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Qui abbiamo spostato la sincronizzazione dalla firma del metodo a un blocco sincronizzato che avvolge la creazione dell'istanza di MySingleton. Ma questo risolve il nostro problema? Bene, non stiamo più bloccando le letture, ma abbiamo anche fatto un passo indietro. Più thread colpiranno il metodo getInstance () contemporaneamente o all'incirca nello stesso tempo e vedranno tutti il ​​membro INSTANCE come null. Colpiranno quindi il blocco sincronizzato dove si otterrà il blocco e creerà l'istanza. Quando quel thread esce dal blocco, gli altri thread contenderanno il blocco e uno per uno ogni thread cadrà nel blocco e creerà una nuova istanza della nostra classe. Quindi siamo tornati da dove siamo partiti.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Qui emettiamo un altro controllo da INSIDE il blocco. Se il membro INSTANCE è già stato impostato, ignoreremo l'inizializzazione. Questo si chiama blocco con doppio controllo.

Questo risolve il nostro problema di istanza multipla. Ma ancora una volta, la nostra soluzione ha presentato un'altra sfida. Altri thread potrebbero non "vedere" che il membro INSTANCE è stato aggiornato. Ciò è dovuto al modo in cui Java ottimizza le operazioni di memoria. I thread copiano i valori originali delle variabili dalla memoria principale nella cache della CPU. Le modifiche ai valori vengono quindi scritte e lette da quella cache. Questa è una funzionalità di Java progettata per ottimizzare le prestazioni. Ma questo crea un problema per la nostra implementazione singleton. Un secondo thread - in fase di elaborazione da una CPU o core diverso, utilizzando una cache diversa - non vedrà le modifiche apportate dal primo. Questo farà sì che il secondo thread vedrà il membro INSTANCE come null forzando la creazione di una nuova istanza del nostro singleton.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Risolviamo questo usando la parola chiave volatile sulla dichiarazione del membro INSTANCE. Questo dirà al compilatore di leggere sempre e scrivere nella memoria principale e non nella cache della CPU.

Ma questo semplice cambiamento ha un costo. Poiché stiamo bypassando la cache della CPU, subiremo un calo delle prestazioni ogni volta che operiamo sul membro instabile INSTANCE, cosa che facciamo 4 volte. Ricontrolliamo l'esistenza (1 e 2), impostiamo il valore (3) e quindi restituiamo il valore (4). Si potrebbe sostenere che questo percorso è il caso marginale poiché creiamo l'istanza solo durante la prima chiamata del metodo. Forse un colpo di performance alla creazione è tollerabile. Ma anche il nostro caso d'uso principale, si legge, opererà sull'elemento volatile due volte. Una volta per verificare l'esistenza e di nuovo per restituire il suo valore.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

Poiché l'hit di performance è dovuto all'operare direttamente sull'elemento volatile, impostiamo una variabile locale sul valore della volatile e operiamo invece sulla variabile locale. Ciò ridurrà il numero di volte in cui operiamo sulla volatile, recuperando così alcune delle nostre prestazioni perse. Nota che dobbiamo reimpostare la nostra variabile locale quando inseriamo il blocco sincronizzato. Ciò garantisce che sia aggiornato con qualsiasi modifica avvenuta mentre stavamo aspettando il blocco.

Ho scritto un articolo su questo recentemente. Decostruire The Singleton . Puoi trovare maggiori informazioni su questi esempi e un esempio del modello "holder" lì. C'è anche un esempio reale che mostra l'approccio volatile ricontrollato. Spero che sia di aiuto.


Potresti spiegare perché BearerToken instancenel tuo articolo non lo è static? E cos'è result.hasExpired()?
Woland,

E che dire class MySingleton- forse dovrebbe essere final?
Woland,

1
@Woland L' BearerTokenistanza non è statica perché fa parte di BearerTokenFactory - che è configurata con un server di autorizzazione specifico. Potrebbero esserci molti BearerTokenFactoryoggetti - ognuno con la propria "cache" BearerTokenche distribuisce fino alla sua scadenza. Il hasExpired()metodo su BeraerTokenviene chiamato nel get()metodo factory per assicurarsi che non distribuisca un token scaduto. Se scaduto, verrà richiesto un nuovo token per il server di autorizzazione. Il paragrafo che segue il blocco di codice spiega questo in modo più dettagliato.
Michael Andrews,

4

Ecco come implementare un semplice singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Ecco come creare correttamente il tuo pigro singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

Entrambi sono pigri, supponendo che l'unica cosa di cui hai bisogno da singleton sia la sua istanza.
Miha_x64,

@ Miha_x64 il primo caso crea un'istanza del singleton quando la JVM inizializza la classe, il secondo caso crea un'istanza del singleton solo quando chiama getInstance(). Ma in effetti se non hai altri metodi statici nella tua classe Singletone chiami solo getInstance()non c'è vera differenza.
Nicolas Filotto,

3

È necessario un doppio controllo del linguaggio se è necessario caricare pigramente la variabile di istanza di una classe. Se è necessario caricare pigramente una variabile statica o un singleton, è necessario inizializzare il linguaggio dell'intestatario della domanda .

Inoltre, se il singleton deve essere seriliazble, tutti gli altri campi devono essere transitori e il metodo readResolve () deve essere implementato per mantenere invariante l'oggetto singleton. Altrimenti, ogni volta che l'oggetto viene deserializzato, verrà creata una nuova istanza dell'oggetto. Ciò che readResolve () fa è sostituire il nuovo oggetto letto da readObject (), che ha costretto quel nuovo oggetto a essere garbage collection in quanto non vi è alcuna variabile che si riferisce ad esso.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 

3

Vari modi per creare un oggetto singleton:

  1. Secondo Joshua Bloch - Enum sarebbe il migliore.

  2. puoi usare anche il doppio controllo del blocco.

  3. È possibile utilizzare anche la classe statica interna.


3

Enum singleton

Il modo più semplice per implementare un Singleton sicuro per i thread è usare un Enum

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Questo codice funziona dall'introduzione di Enum in Java 1.5

Doppio bloccaggio controllato

Se vuoi codificare un singleton "classico" che funziona in un ambiente multithread (a partire da Java 1.5), dovresti usare questo.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

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

Questo non è thread-safe prima di 1.5 perché l'implementazione della parola chiave volatile era diversa.

Caricamento anticipato Singleton (funziona anche prima di Java 1.5)

Questa implementazione crea un'istanza per il singleton quando la classe viene caricata e fornisce la sicurezza del thread.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}

2

Per JSE 5.0 e versioni successive adottare l'approccio Enum, altrimenti utilizzare l'approccio statico del singleton holder ((un approccio di caricamento lento descritto da Bill Pugh). La soluzione Latter è anche sicura per i thread senza richiedere costrutti di linguaggio speciali (ovvero volatile o sincronizzato).


2

Un altro argomento spesso usato contro i Singleton sono i loro problemi di testabilità. I singleton non sono facilmente derisibili a scopo di test. Se questo risulta essere un problema, mi piace apportare la seguente leggera modifica:

public class SingletonImpl {

    private static SingletonImpl instance;

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

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

Il setInstancemetodo aggiunto consente di impostare un'implementazione mockup della classe singleton durante il test:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Questo funziona anche con approcci di inizializzazione precoce:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Ciò ha lo svantaggio di esporre questa funzionalità anche alla normale applicazione. Altri sviluppatori che lavorano su quel codice potrebbero essere tentati di usare il metodo "setInstance" per alterare una funzione specifica e quindi cambiare l'intero comportamento dell'applicazione, quindi questo metodo dovrebbe contenere almeno un buon avviso nel suo javadoc.

Tuttavia, per la possibilità di test di simulazione (quando necessario), questa esposizione al codice può essere un prezzo accettabile da pagare.


1

classe singleton più semplice

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

1
questa è la stessa della risposta di Jonathan qui sotto
o

1
Duplica di questa risposta tra fratelli di Jonathan pubblicata cinque anni prima. Vedi quella risposta per commenti interessanti.
Basil Bourque,

0

Penso ancora che dopo java 1.5, enum sia la migliore implementazione singleton disponibile in quanto garantisce anche che negli ambienti multi-thread - venga creata solo un'istanza.

public enum Singleton{ INSTANCE; }

e il gioco è fatto !!!


1
Questo è già menzionato in altre risposte anni fa.
Feci il

0

Dai un'occhiata a questo post.

Esempi di modelli di progettazione GoF nelle librerie principali di Java

Dalla sezione "Singleton" della migliore risposta,

Singleton (riconoscibile con metodi di creazione che restituiscono sempre la stessa istanza (di solito di se stesso))

  • java.lang.Runtime # getRuntime ()
  • java.awt.Desktop # getDesktop ()
  • java.lang.System # getSecurityManager ()

Puoi anche imparare l'esempio di Singleton dalle stesse classi native di Java.


0

Il miglior modello singleton che abbia mai visto utilizza l'interfaccia del fornitore.

  • È generico e riutilizzabile
  • Supporta l'inizializzazione lazy
  • Viene sincronizzato solo fino a quando non viene inizializzato, quindi il fornitore bloccante viene sostituito con un fornitore non bloccante.

Vedi sotto:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}

-3

A volte un semplice "static Foo foo = new Foo(); " non è abbastanza. Pensa solo ad alcuni inserimenti di dati di base che vuoi fare.

D'altra parte dovresti sincronizzare qualsiasi metodo che istanzia la variabile singleton in quanto tale. La sincronizzazione non è male come tale, ma può portare a problemi di prestazioni o blocco (in casi molto rari usando questo esempio. La soluzione è

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

Ora che succede? La classe viene caricata tramite il caricatore di classi. Immediatamente dopo che la classe è stata interpretata da un array di byte, la VM esegue il blocco statico {} . questo è l'intero segreto: il blocco statico viene chiamato solo una volta, il momento in cui la classe (nome) del pacchetto dato viene caricata da questo caricatore di una classe.


3
Non vero. Le variabili statiche vengono inizializzate insieme ai blocchi statici quando viene caricata la classe. Non è necessario dividere la dichiarazione.
Craig P. Motlin,

-5
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

Dato che abbiamo aggiunto la parola chiave Synchronized prima di getInstance, abbiamo evitato le condizioni di competizione nel caso in cui due thread chiamassero getInstance contemporaneamente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.