Thread multitons sicuri in Java

Data la seguente multiton:

public class Multiton { private static final Multiton[] instances = new Multiton[...]; private Multiton(...) { //... } public static Multiton getInstance(int which) { if(instances[which] == null) { instances[which] = new Multiton(...); } return instances[which]; } } 

Come possiamo mantenerlo thread-safe e pigro senza la costosa sincronizzazione del metodo getInstance () e la controversia del blocco a doppio controllo? Qui viene menzionato un modo efficace per i singleton, ma ciò non sembra estendersi ai multitoni.

Ciò fornirà un meccanismo di archiviazione sicuro per i tuoi multitoni. L’unico svantaggio è che è ansible creare un Multiton che non verrà utilizzato nella chiamata putIfAbsent () . La possibilità è piccola ma esiste. Ovviamente sulla remota possibilità che accada, non provoca comunque danni.

Tra gli aspetti positivi, non è richiesta alcuna preallocazione o inizializzazione e non sono previste restrizioni dimensionali predefinite.

 private static ConcurrentHashMap instances = new ConcurrentHashMap(); public static Multiton getInstance(int which) { Multiton result = instances.get(which); if (result == null) { Multiton m = new Multiton(...); result = instances.putIfAbsent(which, m); if (result == null) result = m; } return result; } 

AGGIORNAMENTO: con Java 8, può essere ancora più semplice:

 public class Multiton { private static final ConcurrentMap multitons = new ConcurrentHashMap<>(); private final String key; private Multiton(String key) { this.key = key; } public static Multiton getInstance(final String key) { return multitons.computeIfAbsent(key, Multiton::new); } } 

Mmm va bene!


RISPOSTA ORIGINALE

Questa è una soluzione che si basa sul pattern Memoizer come descritto in JCiP . Utilizza una ConcurrentHashMap come una delle altre risposte, ma invece di memorizzare direttamente le istanze Multiton, che possono portare alla creazione di istanze inutilizzate, memorizza il calcolo che porta alla creazione del Multiton. Quel livello aggiuntivo risolve il problema delle istanze inutilizzate.

 public class Multiton { private static final ConcurrentMap> multitons = new ConcurrentHashMap<>(); private static final Callable creator = new Callable() { public Multiton call() { return new Multiton(); } }; private Multiton(Strnig key) {} public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException { Future f = multitons.get(key); if (f == null) { FutureTask ft = new FutureTask<>(creator); f = multitons.putIfAbsent(key, ft); if (f == null) { f = ft; ft.run(); } } return f.get(); } } 

È ansible utilizzare una serie di blocchi, per poter essere in grado di ottenere contemporaneamente istanze diverse:

 private static final Multiton[] instances = new Multiton[...]; private static final Object[] locks = new Object[instances.length]; static { for (int i = 0; i < locks.length; i++) { locks[i] = new Object(); } } private Multiton(...) { //... } public static Multiton getInstance(int which) { synchronized(locks[which]) { if(instances[which] == null) { instances[which] = new Multiton(...); } return instances[which]; } } 

Con l’avvento di Java 8 e alcuni miglioramenti in ConcurrentMap e Lambdas è ora ansible implementare un Multiton (e probabilmente anche un Singleton ) in un modo molto più ordinato:

 public class Multiton { // Map from the index to the item. private static final ConcurrentMap multitons = new ConcurrentHashMap<>(); private Multiton() { // Possibly heavy construction. } // Get the instance associated with the specified key. public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException { // Already made? Multiton m = multitons.get(key); if (m == null) { // Put it in - only create if still necessary. m = multitons.computeIfAbsent(key, k -> new Multiton()); } return m; } } 

Sospetto – anche se mi farebbe sentire a disagio – che getInstance potrebbe essere ulteriormente ridotto a:

 // Get the instance associated with the specified key. public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException { // Put it in - only create if still necessary. return multitons.computeIfAbsent(key, k -> new Multiton()); } 

Stai cercando un AtomicReferenceArray .

 public class Multiton { private static final AtomicReferenceArray instances = new AtomicReferenceArray(1000); private Multiton() { } public static Multiton getInstance(int which) { // One there already? Multiton it = instances.get(which); if (it == null) { // Lazy make. Multiton newIt = new Multiton(); // Successful put? if ( instances.compareAndSet(which, null, newIt) ) { // Yes! it = newIt; } else { // One appeared as if by magic (another thread got there first). it = instances.get(which); } } return it; } }