[Java] Performances en Java - Singleton et synchronisation

Test du pattern Singleton, avec la notion d'accès synchronisés. Ce test met en œuvre trois solutions :

  • La classe A utilise un getInstance synchronisé
  • La classe B utilise un getInstance non-synchronisé, où seul la partie d'instanciation est synchronisée
  • La classe C ne se soucie pas de la synchronisation car l'instantiation est statique
 public class SynchronizeBench2 {
   static class A {
     private static A instance = null;
     private A() {
       System.out.println("New A");
     }
     public static synchronized A getInstance() {
       if (instance == null) {
         instance = new A();
       }
       return instance;
     }
   }
   static class B {
     private static B instance = null;
     private B() {
       System.out.println("New B");
     }
     public static B getInstance() {
       if (instance == null) {
         synchronized (B.class) {
           if (instance == null) {
             instance = new B();
           }
         }
       }
       return instance;
     }
   }
   static class C {
     private static C instance = new C();
     private C() {
       System.out.println("New C");
     }
     public static C getInstance() {
       return instance;
     }
   }
   public static void main(String args) {
 
     int i = 10000000;
     long time = System.currentTimeMillis();
     while (i > 0) {
       A.getInstance();
       i--;
     }
     System.out.println("Test A : "+(int)(System.currentTimeMillis()-time)+" ms");
 
     i = 10000000;
     time = System.currentTimeMillis();
     while (i > 0) {
       B.getInstance();
       i--;
     }
     System.out.println("Test B : "+(int)(System.currentTimeMillis()-time)+" ms");
 
     i = 10000000;
     time = System.currentTimeMillis();
     while (i > 0) {
       C.getInstance();
       i--;
     }
     System.out.println("Test C : "+(int)(System.currentTimeMillis()-time)+" ms");
 
   }
 
 }

Résultats

  • Classe A : entre 327 et 350 millisecondes
  • Classe B : entre 62 et 73 millisecondes
  • Classe C : entre 94 et 109 millisecondes

On remarque que la première solution est de loin la plus mauvaise : synchroniser la méthode entière oblige la machine virtuelle a mettre en œuvre le principe de synchronisation à chaque appel, ce qui dégrade les performances. La seconde méthode est bien meilleure, le bloc synchronisé étant accédé uniquement si l'instance n'a pas été crée. Bizarrement, la dernière méthode n'est pas la plus performante alors que théoriquement elle devrais être la plus rapide (moins de test et pas de synchronisation).

[Java] Performances en Java - L'influcence du mot clé synchronized (1)

Le mot clé synchronized permet de s'assurer qu'une méthode ou un bloc de code se sera pas accédé par deux Thread en même temps. Pour plus de détails, voyez cet article sur Développez.com[htt.... Voici le test effectué :

public class SynchronizeBench {
 
    public SynchronizeBench() {
    }
 
    public void a() {
    }
 
    public synchronized void b() {
    }
 
    static void test(SynchronizeBench bench, boolean a) {
      int i = 500000;
      long time = System.currentTimeMillis();
      while (i > 0) {
        if (a) {
          bench.a();
        } else {
          bench.b();
        }
        i--;
      }
      time = System.currentTimeMillis() - time;
      System.out.println("Methode "+(a?"a":"b")+" : "+time+" ms");
    }
 
    public static void main(String[] args) {
      SynchronizeBench bench = new SynchronizeBench();
      test(bench, true);
      test(bench, false);
    }
 
  }

Résultats

  • Méthode a (sans synchronisation) : 0 miilisecondes
  • Méthode b (avec synchronisation) : entre 14 et 17 millisecondes

Conclusion

L'utilisation du mot clé synchronized ralenti en soi l'accès à une méthode.

[Java] Performances en Java - La construction de String

La construction de chaînes de caractères en Java peut être optimisée en utilisant les nouvelles classes incluses dans le JDK 1.5. Ce petit test compare trois méthodes différentes pour construire une chaîne de caractère de 50 000 entiers : - La méthode classique par concaténation de String (opérateur +=) - La classe StringBuilder, qui est spécialisée dans la construction de chaînes - La classe StringBuffer, qui est identique au StringBuilder avec en plus la notion de synchronisation

Voici le code utilisé :

static Runnable classic = new Runnable() {
    @Override
    public void run() {
      String s = "";
      for (int i = 0; i < 50000; i++) {
        int v = (int)(Math.random() * 9f);
        s += v;
      }
    }	
  };
 
  static Runnable stringBuilder = new Runnable() {
    @Override
    public void run() {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < 50000; i++) {
        int v = (int)(Math.random() * 9f);
        sb.append(v);
      }
    }
  };
 
  static Runnable stringBuffer = new Runnable() {
    @Override
    public void run() {
      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < 50000; i++) {
        int v = (int)(Math.random() * 9f);
        sb.append(v);
      }
    }
  };
 
  static void test(Runnable runnable, String msg) {
    long time = System.currentTimeMillis();
    runnable.run();
    time = System.currentTimeMillis() - time;
    System.out.println(msg+" : "+time+" ms");
  }
 
  public static void main(String[] args) {
    System.out.println("TEST PERFORMANCES String");
    test(classic, "Methode classique");
    test(stringBuilder, "Methode StringBuilder");
    test(stringBuffer, "Methode StringBuffer");
  }

Les résultats sont sans équivoques :

  • Méthode classique : entre 2000 et 2200 millisecondes
  • Méthode StringBuilder : entre 0 et 16 millisecondes
  • Méthode StringBuffer : entre 0 et 16 millisecondes

La méthode classique est véritablement bien plus lente. Concernant les deux méthodes StringBuilder et StringBuffer, il est difficile de les différencier avec ce test, mais elles sont néanmoins bien plus performantes que la méthode classique.

 Ce test a été réalisé sur un pc Intel Core2 Duo 2.66GHz
 avec 2Go de RAM, Windows XP SP3 et JRE 1.6.0_07.