Support d'initialisation à la demande
En génie logiciel, le patron de conception Support d'initialisation à la demande (initialization on demand holder) est un singleton à initialisation tardive. Il peut être implémenté aussi bien en environnement séquentiel ou mono-thread que dans un environnement concurrent, même si, dans ce dernier cas, un soin particulier doit y être apporté pour s'assurer que son implémentation est correcte.
Exemple d'implémentation en Java
Cette implémentation fonctionne correctement en environnement concurrent avec toutes les versions de Java, et elle est assez performante. L'implémentation originale est de Bill Pugh (cf. liens ci-dessous), et elle a été modifiée pour rendre ChargeurALaDemande.instance privé et final.
public class Singleton {
private Singleton() {
}
private static class ChargeurALaDemande {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return ChargeurALaDemande.instance;
}
}
Comment cela fonctionne-t-il ?
L'implémentation repose sur la phase d'initialisation d'une classe par la machine virtuelle Java, qui est très bien spécifiée (cf. section 12.4 de la Java Language Specification (JLS) pour de plus amples détails).
Lorsque la classe Singleton est chargée par la JVM, la classe est initialisée. Puisque la classe ne définit aucune variable statique, la phase d'initialisation se termine très rapidement. La classe statique ChargeurALaDemande définie en son sein n'est pas initialisée tant que la JVM estime qu'elle n'a pas besoin de cette classe. Elle ne l'est que lorsque la méthode statique getInstance est invoquée sur la classe Singleton : à la première invocation de cette méthode, la JVM chargera et initialisera alors la classe ChargeurALaDemande.
L'initialisation de la classe ChargeurALaDemande consiste à initialiser sa variable statique instance en exécutant le constructeur (privé) de la classe englobante Singleton. Puisque la JLS garantit que la phase d'initialisation d'une classe JLS est séquentielle et synchronisée, c'est-à-dire non-concurrente, aucune synchronisation supplémentaire n'est requise dans l'implémentation de la méthode statique getInstance lors de la phase de chargement et d'initialisation. Et puisque la phase d'initialisation modifie la variable statique instance de manière séquentielle, toutes les invocations concurrentes de la méthode getInstance qui suivent retourneront la même instance correctement initialisée sans aucun surcoût lié à la synchronisation.
Quand l'utiliser ?
Le point fondamental de ce patron est la suppression sans danger du surcoût lié à la synchronisation associée à l'accès à l'instance unique d'un singleton. Il est recommandé de l'utiliser dès que :
- le coût de l'initialisation de la classe est élevé,
- la classe ne peut être initialisée de manière sûre lorsqu'elle est chargée,
- l'initialisation de la classe fait très largement accès à des données concurrentes.
Voir aussi
- Patron de conception singleton
- Antipattern Double-checked locking
- Patron de conception multiton
Liens externes
- http://www.cs.umd.edu/~pugh/java/memoryModel/
- http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
- http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html