2010-06-02 7 views
8

Je viens avec cette question lors de l'implémentation du modèle singleton en Java. Même si l'exemple ci-dessous n'est pas mon vrai code, pourtant très similaire à l'original.Comment synchroniser la méthode statique dans Java

public class ConnectionFactory{ 
    private static ConnectionFactory instance; 

    public static synchronized ConnectionFactory getInstance(){ 
     if(instance == null){ 
      instance = new ConnectionFactory(); 
     } 

     return instance; 
    } 

    private ConnectionFactory(){ 
     // private constructor implementation 
    } 
} 

Parce que je ne suis pas tout à fait sûr du comportement d'une méthode synchronisée statique, je reçois une suggestion de Google - ne pas (ou le moins possible) plusieurs méthodes synchronisées statiques dans la même classe. Je suppose que lors de l'implémentation de la méthode synchronisée statique, un verrou appartenant à l'objet Class est utilisé afin que plusieurs méthodes synchronisées statiques puissent dégrader les performances du système.

Ai-je raison? ou JVM utiliser un autre mécanisme pour implémenter la méthode statique synchronisée? Quelle est la meilleure pratique si je dois implémenter plusieurs méthodes synchronisées statiques dans une classe?

Merci à tous!

Cordialement!

+2

Votre code réel également préoccupé par l'initialisation paresseuse? Parce que c'est généralement un gaspillage de code et de temps de développement; initialiser le champ dans sa déclaration est la bonne chose à faire 99% du temps et rend la synchronisation inutile. –

Répondre

7

La meilleure approche (ce qui fait que peu de changements dans votre code que possible) est de faire comme ceci:

public class ConnectionFactory{ 
    private static ConnectionFactory instance = new ConnectionFactory(); 

    public static ConnectionFactory getInstance(){ 
     return instance; 
    } 

    private ConnectionFactory(){ 
    } 
} 

Comme vous pouvez le voir, il n'y a pas de réel besoin dans getInstance méthode maintenant, afin que vous puissiez simplifier le code à:

public class ConnectionFactory{ 
    public static final ConnectionFactory INSTANCE = new ConnectionFactory(); 

    private ConnectionFactory(){ 
    } 
} 

UPD sur la synchronisation: la meilleure façon est la synchronisation sur un verrou qui ne soit pas visible à des classes externes, à savoir:

public class ConnectionFactory{ 
    private static final Object lock = new Object(); 

    public static void doSmth() { 
     synchronized (lock) { 

      ... 
     } 
    } 

    public static void doSmthElse() { 
     synchronized (lock) { 

      ... 
     } 
    } 
} 

Il ya beaucoup de discussions sur "pourquoi la synchronisation sur this est une mauvaise idée" (comme this one), je pense qu'il en est de même pour la synchronisation en classe.

+0

comment créerait-il 'instance', si le seul constructeur lançait une exception? – unbeli

+0

@unbeli: c'était mon mauvais, corrigé. Mais si vous n'avez même pas besoin d'une instance si c'était une solution correcte. – Roman

+0

@unbeli: en utilisant une méthode d'usine statique privée ou un bloc d'initialisation statique. Malheureusement, toutes ces discussions sur le modèle de mémoire Java et le double contrôle ont pollué l'Internet avec des centaines d'échantillons de code qui donnent aux novices l'impression que l'initialisation paresseuse de singletons dans la méthode getInstance() est une bonne idée ou même la norme. –

2

Oui, les méthodes statiques sont synchronisées sur leur objet de classe. Je ne m'inquiéterais pas de la performance ici, car probablement ce ne sera pas votre point chaud de performance. Faites-le simplement, optimisez quand et où vous le souhaitez.

2

Les méthodes synchronisées statiques utilisent le verrou sur la classe. Dans le cas de votre exemple, il accèderait au verrou de l'objet de classe ConnectionFactory. La meilleure pratique consiste à ne pas garder les verrous plus longtemps que nécessaire. Si vous avez plusieurs méthodes synchronisées n'est pas un problème en soi.

3

Il existe plusieurs façons de créer un singleton.

Une méthode recommandée est d'utiliser un ENUM (garanti pour créer une seule instance):

public enum ConnectionFactory { 

    INSTANCE; 

} 

Ou vous pouvez créer statiquement lorsque les charges de classe:

public class ConnectionFactory { 

    private static ConnectionFactory INSTANCE = new ConnectionFactory(); 

    private ConnectionFactory() {} 

    public static ConnectionFactory getInstance() { 
    return INSTANCE; 
    }  

} 

Si vous devez charger paresseusement il vous pouvez utiliser cet idiome (plutôt que le double checked locking anti-pattern)

public class ConnectionFactory { 

    private static class ConnectionFactoryHolder { 
    private static ConnectionFactory INSTANCE = new ConnectionFactory(); 
    } 

    public static ConnectionFactory getInstance() { 
    return ConnectionFactoryHolder.INSTANCE; 
    } 

} 
0

Effective Java recommande d'utiliser Enums pour créer un singleton.Donc, votre code ressemblerait à quelque chose comme ceci:

public enum ConnectionFactory{ 
INSTANCE; 

// Other factory methods go here. 

} 

}

Questions connexes