Ce singleton Java peut-il être reconstruit à plusieurs reprises dans WebSphere 6?

StackOverflow https://stackoverflow.com/questions/225895

  •  03-07-2019
  •  | 
  •  

Question

J'essaie de détecter un problème dans notre système et le code suivant m'inquiète. Les événements suivants se produisent dans notre méthode doPost () dans la servlet principale (les noms ont été modifiés pour protéger les coupables):

...
if(Single.getInstance().firstTime()){
   doPreperations();
}
normalResponse();
...

Le singleton 'Single' ressemble à ceci:

private static Single theInstance = new Single();

private Single() {
...load properties...
}

public static Single getInstance() {
    return theInstance;
}

Avec la manière dont il est configuré pour utiliser un initialiseur statique au lieu de rechercher une valeur null theInstance dans la méthode getInstance (), cette opération pourrait-elle être reconstruite maintes et maintes fois?

PS - Nous utilisons WebSphere 6 avec l’application Java 1.4

.
Était-ce utile?

La solution

J'ai trouvé ceci sur le site de Sun:

  

Plusieurs singletons chargés simultanément par différents chargeurs de classe

     

Lorsque deux chargeurs de classe chargent une classe,   vous avez en fait deux copies du   classe, et chacun peut avoir son propre   Instance Singleton. C'est   particulièrement utile dans les servlets   fonctionnant dans certains moteurs de servlet   (iPlanet par exemple), où chaque   servlet utilise par défaut sa propre classe   chargeur. Deux servlets différentes   accéder à un Singleton conjoint sera, en   fait, obtenez deux objets différents.

     

Plusieurs chargeurs de classe se produisent plus   généralement que vous ne le pensez. Quand   les navigateurs chargent les classes du réseau   pour les applets, ils utilisent un   chargeur de classe séparé pour chaque serveur   adresse. De même, Jini et RMI   les systèmes peuvent utiliser une classe séparée   chargeur pour les différentes bases de code   à partir duquel ils téléchargent des fichiers de classe.   Si votre propre système utilise une classe personnalisée   chargeurs, tous les mêmes problèmes peuvent   se poser.

     

Si chargé par différents chargeurs de classe,   deux classes avec le même nom, même   le même nom de package, sont traités comme   distinctes - même si, en fait, elles sont   octet pour octet de la même classe. le   différents chargeurs de classe représentent   différents espaces de noms qui distinguent   classes (même si les classes   les noms sont les mêmes), de sorte que les deux   Les classes MySingleton sont en fait   distinct. (Voir " Class Loaders en tant que   Mécanisme d'espace de noms " dans les ressources.)   Depuis deux objets Singleton appartiennent à   deux classes du même nom, ce sera   semble à première vue qu'il y a   deux objets singleton du même   classe.

Citation .

Outre le problème ci-dessus, si firstTime () n'est pas synchronisé, vous risquez également d'avoir des problèmes de threading.

Autres conseils

Non, il ne sera pas construit encore et encore. C'est statique, donc ça ne sera construit qu'une fois, juste quand la classe est touchée pour la première fois par le chargeur de classe.

Seule exception - si vous avez plusieurs chargeurs de classe.

(de GeekAndPoke ):

alt text

Comme d'autres l'ont mentionné, l'initialiseur statique ne sera exécuté qu'une fois par chargeur de classe.

Une chose que je voudrais examiner est la méthode firstTime () - pourquoi le travail dans doPreparations () ne peut-il pas être traité dans le singleton lui-même?

Cela ressemble à un mauvais ensemble de dépendances.

Il n’ya absolument aucune différence entre l’initialisation statique et l’initialisation différée. En fait, il est beaucoup plus facile de gâcher l'initialisation paresseuse, qui impose également la synchronisation. La machine virtuelle Java garantit que l’initialiseur statique est toujours exécuté avant l’accès à la classe, ce qui ne se produira qu’une fois.

Cela dit, JVM ne garantit pas que votre classe ne sera chargée qu'une seule fois. Toutefois, même si elle est chargée plusieurs fois, votre application Web ne verra toujours que le singleton pertinent, car elle sera chargée dans le chargeur de classes de l'application Web ou dans son parent. Si plusieurs applications Web sont déployées, firstTime () sera appelé une fois pour chaque application.

La chose la plus évidente à vérifier est que firstTime () doit être synchronisé et que le drapeau firstTime est défini avant de quitter cette méthode.

Non, cela ne créera pas plusieurs copies de 'Single'. (Le problème du chargeur de classe sera visité ultérieurement)

L’implémentation que vous avez décrite est décrite sous le nom "Eager Initialization" dans le livre de Briant Goetz - " sur la concurrence Java en pratique '.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {
        return theInstance;
    }
}

Cependant, le code n'est pas ce que vous vouliez. Votre code tente d'effectuer une initialisation différée après la création de l'instance. Cela nécessite que toute la bibliothèque cliente effectue 'firstTime () / doPreparation ()' avant de l'utiliser. Vous comptez sur le client pour faire ce qui est bien, ce qui rend le code très fragile.

Vous pouvez modifier le code comme suit pour éviter tout code en double.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {   
        // check for initialization of theInstance
        if ( theInstance.firstTime() )
           theInstance.doPreparation();

        return theInstance;
    }
}

Malheureusement, il s’agit d’une mauvaise implémentation de l’initialisation différée qui ne fonctionnera pas dans un environnement concurrent (comme le conteneur J2EE).

Il existe de nombreux articles sur l’initialisation Singleton, en particulier sur le modèle de mémoire. JSR 133 a corrigé de nombreuses faiblesses en mémoire Java. modèle en Java 1.5 et 1.6.

Avec Java 1.5 & amp; 1.6, vous avez plusieurs choix et ils sont mentionnés dans le livre ' Effective Java ' par Joshua Bloch.

  1. Eager Initialziation, comme ci-dessus [EJ Item 3]
  2. Idiome de la classe des détenteurs d’une initalisation paresseuse [EJ Item 71]
  3. Type d'énumération [élément EJ 3]
  4. Verrouillage à double contrôle avec champ statique 'volatil' [EJ Item 71]

Les solutions 3 et 4 ne fonctionneront qu’en Java 1.5 et supérieur. La meilleure solution serait donc la deuxième.

Voici la mise en oeuvre de psuedo.

public class Single
{
    private static class SingleHolder
    {
        public static Single theInstance = new Single();
    }

    private Single() 
    { 
        // load properties
        doPreparation();
    }

    public static Single getInstance() 
    {
        return SingleHolder.theInstance;
    }
}

Notez que 'doPreparation ()' est à l'intérieur du constructeur, vous avez donc la garantie d'obtenir l'instance correctement initialisée. De plus, vous récupérez le chargement de classe paresseux de la machine virtuelle Java et n’avez besoin d’aucune synchronisation 'getInstance ()'.

Vous avez remarqué que le champ statique theInstance n'est pas "final". L'exemple de Java Concurrency n'a pas "final", mais EJ en a. Peut-être que James peut ajouter plus de couleur à sa réponse sur "classloader" et sur l'exigence de "final" pour garantir l'exactitude,

Cela dit, il y a un effet secondaire lié à l'utilisation de "statique finale". Le compilateur Java est très agressif quand il voit la 'finale statique' et essaie de le mettre en ligne autant que possible. Ceci est mentionné sur un blog de Jeremy Manson .

Voici un exemple simple.

fichier: A.java

public class A
{
    final static String word = "Hello World";
}

fichier: B.java

public class B
{
    public static void main(String[] args) {
        System.out.println(A.word);
    }
}

Après avoir compilé A.java et B.java, vous remplacez A. java par ce qui suit.

fichier: A.java

public class A
{
    final static String word = "Goodbye World";
}

Vous recompilez 'A.java' et relancez B.class. La sortie que vous obtiendrez est

Hello World

En ce qui concerne le problème du chargeur de classe, la réponse est oui, vous pouvez avoir plusieurs instances de Singleton dans plusieurs chargeurs de classe. Vous pouvez trouver plus d'informations sur wikipedia . Il existe également un article spécifique sur Websphere .

La seule chose que je voudrais changer à propos de cette implémentation de Singleton (à part ne pas utiliser de Singleton du tout) est de rendre le champ d'instance final. Le champ statique sera initialisé une fois, lors du chargement de la classe. Comme les classes sont chargées paresseusement, vous bénéficiez d’une instanciation paresseuse gratuite.

Bien sûr, s’il est chargé à partir de chargeurs de classe distincts, vous obtenez plusieurs "singletons", mais c’est une limitation de chaque idiome singleton en Java.

MODIFIER : les bits firstTime () et doPreparations () semblent suspects. Ne peuvent-ils pas être déplacés dans le constructeur de l'instance singleton?

Non - l'initialisation statique de l'instance ne sera effectuée qu'une fois. Deux choses à considérer:

  • Ceci n'est pas thread-safe (l'instance n'est pas "publiée" dans la mémoire principale)
  • Votre méthode firstTime est probablement appelée plusieurs fois, à moins d'être correctement synchronisée

En théorie, il ne sera construit qu’une fois. Cependant, ce modèle se brise dans divers serveurs d’application, où vous pouvez obtenir plusieurs instances de classes 'singleton' (car elles ne sont pas thread-safe).

De plus, le motif singleton a été très critiqué. Voir par exemple Singleton, je t'aime, mais tu me rabats

Ceci ne sera chargé qu'une seule fois lorsque la classe est chargée par le chargeur de classes. Cet exemple fournit une meilleure implémentation Singleton, cependant, il est aussi chargé paresseux que possible et sécurisé pour les threads. De plus, cela fonctionne dans toutes les versions connues de Java. Cette solution est la plus portable sur différents compilateurs et machines virtuelles Java.


public class Single {

private static class SingleHolder {
   private static final Single INSTANCE = new Single();
}

private Single() {
...load properties...
}

public static Single getInstance() {
    return SingleHolder.INSTANCE;
}

}

La classe interne n'est référencée pas plus tôt (et donc chargée pas plus tôt par le chargeur de classes) que l'instant où getInstance () est appelée. Ainsi, cette solution est sécurisée pour les threads sans nécessiter de construction de langage spéciale (c'est-à-dire volatile et / ou synchronisée).

Il n'est pas obligatoire que l'instance soit définitive (ce n'est pas une bonne idée, car cela vous évitera de changer de comportement en utilisant d'autres modèles).

Dans le code ci-dessous, vous pouvez voir comment il n'est instancié qu'une seule fois (première fois que vous appelez le constructeur)

date du package;

importer java.util.Date;

la classe publique USDateFactory implémente DateFactory {     statique privée USDateFactory usdatefactory = null;

private USDateFactory () { }

public static USDateFactory getUsdatefactory() {
    if(usdatefactory==null) {
        usdatefactory = new USDateFactory();
    }
    return usdatefactory;
}

public String getTextDate (Date date) {
    return null;
}

public NumericalDate getNumericalDate (Date date) {
    return null;
}

}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top