2015-11-12 11 views
7

Nel fare l'inizializzazione pigra di un Singleton statica in Java si può fare questo:Possiamo ottenere un caricamento pigro indolore di un membro Java in modo simile al modo in cui possiamo farlo con i singleton statici?

public class Bob { 

    private static class SingletonWrapper { 
     private static final Bob instance = new Bob(); 
    } 

    public static Bob getInstance() { 
     return SingletonWrapper.instance; 
    } 

} 

Perché la classe interna SingletonWrapper viene caricata solo quando prima accede alla Bob() non viene creato fino getInstance() si chiama.

La mia domanda è se esistono trucchi simili che possono essere utilizzati per eseguire un'istanza lazy di una variabile membro in un contesto non statico.

public class Bob { 

    // Clearly this doesn't work as not lazy 
    private final InnerWrapper wrapper = new InnerWrapper(); 

    private class InnerWrapper { 
     private final Jane jane = new Jane(); 
    } 

    public Jane getJane() { 
     return wrapper.jane; 
    } 

} 

C'è un modo possiamo avere un esempio di Jane all'interno Bob e infilare sicuro avere l'istanza creato solo su richiesta senza utilizzare interblocco ricontrollato o AtomicReference. Idealmente, il metodo get dovrebbe rimanere semplice come quello di questi esempi, ma se ciò non è possibile, l'esecuzione più semplice e più rapida possibile (più efficiente) del metodo get sarebbe l'ideale.

+0

@assylias Come il commento dice che non sarebbe fare l'inizio pigro. Non appena crei un 'Bob', crei sempre e immediatamente un' Jane'. –

+0

I miei cattivi ho letto troppo velocemente - qualche motivo per cui non vuoi un AtomicReference? Potrebbe avere prestazioni migliori rispetto all'opzione CHM. – assylias

+0

@assylias Nessun motivo specifico. Fondamentalmente voglio solo sapere quali sono le mie opzioni - considerando quanto sia semplice ed efficace il trucco del wrapper statico per l'inizializzazione pigra dei singletons è un peccato non riuscire a trovare qualcosa di simile per l'inizializzazione pigra dei membri, –

risposta

8

No, non esistono regole di sincronizzazione per i tipi di istanziazione come per le classi di inizializzazione. Devi aggiungerli tu stesso. Che tu lo faccia con un doppio blocco di controllo o qualche altro meccanismo dipende da te.

A partire da Java 8, mi piace usare ConcurrentHashMap#computeIfAbsent per raggiungere la pigrizia.

class Bob { 
    private final ConcurrentHashMap<String, Jane> instance = new ConcurrentHashMap<>(1); 

    public Jane getJane() { 
     return instance.computeIfAbsent("KEY", k -> new Jane()); // use whatever constant key 
    } 
} 

Ci sono anche these solutions per inizializzazione ritardata senza la costrizione filo di sicurezza. Non ero in grado di adattarli in modo pulito per un contesto con multithreading.

Come indicato nei commenti, il blocco con doppia verifica sarà sempre più veloce di queste soluzioni in quanto non include tutto il fluff per nascondere l'implementazione.

+0

Mentre quello è codice pulito, come si confronta il rendimento? –

+0

Il doppio blocco controllato sarebbe probabilmente il più veloce in quanto non ci sono controlli extra. Stai facendo al massimo ciò che devi fare per ottenere l'istanza. –

+1

Molto meglio della mia verbosa soluzione che usa Guava, anche se la logica è simile –

2

È possibile utilizzare la cache di Guava:

public class Bob { 
    private final static Object KEY = new Object(); 

    private final Cache<Object, Jane> cache = 
     CacheBuilder.newBuilder() 
        .build(new CacheLoader<Object, Jane>() { 
           @Override 
           public Jane load() { 
            return new Jane(); 
           } 
          }); 

    public Jane getJane() { 
     return cache.get(KEY); 
    } 

} 
+0

Questo non è il caso d'uso principale di una cache: sfratto automatico. Una concurrentHashMap farà il lavoro e funzionerà bene rispetto alla cache guava – Zava

Problemi correlati