Il s'agit d'un exemple de manuel " rugosité de la serrure "et peut avoir été fait pour obtenir une augmentation des performances.
Considérez ces deux extraits :
StringBuffer b = new StringBuffer();
for(int i = 0 ; i < 100; i++){
b.append(i);
}
contre :
StringBuffer b = new StringBuffer();
synchronized(b){
for(int i = 0 ; i < 100; i++){
b.append(i);
}
}
Dans le premier cas, le StringBuffer doit acquérir et libérer un verrou 100 fois (parce que append
est une méthode synchronisée), alors que dans le second cas, le verrou est acquis et libéré une seule fois. Cela peut vous donner un gain de performance et c'est probablement la raison pour laquelle l'auteur l'a fait. Dans certains cas, le compilateur peut effectuer cette opération rugosité de la serrure pour vous (mais pas autour des constructions en boucle car vous pourriez finir par retenir un verrou pendant de longues périodes).
À propos, le compilateur peut détecter qu'un objet ne "s'échappe" pas d'une méthode et ainsi supprimer complètement l'acquisition et la libération de verrous sur l'objet (élision de verrous) puisqu'aucun autre thread ne peut accéder à l'objet de toute façon. De nombreux travaux ont été réalisés sur ce sujet dans le cadre du projet JDK7 .
Mise à jour :
J'ai effectué deux tests rapides :
1) SANS ÉCHAUFFEMENT :
Dans ce test, je n'ai pas exécuté les méthodes plusieurs fois pour "chauffer" la JVM. Cela signifie que le compilateur du serveur Java Hotspot n'a pas eu l'occasion d'optimiser le code, par exemple en éliminant les verrous pour les objets d'échappement.
JDK 1.4.2_19 1.5.0_21 1.6.0_21 1.7.0_06
WITH-SYNC (ms) 3172 1108 3822 2786
WITHOUT-SYNC (ms) 3660 801 509 763
STRINGBUILDER (ms) N/A 450 434 475
Avec le JDK 1.4, le code avec le bloc synchronisé externe est plus rapide. Cependant, avec le JDK 5 et plus, le code sans synchronisation externe l'emporte.
2) AVEC ÉCHAUFFEMENT :
Dans ce test, les méthodes ont été exécutées plusieurs fois avant que les temps ne soient calculés. Ceci a été fait afin que la JVM puisse optimiser le code en effectuant une analyse d'échappement.
JDK 1.4.2_19 1.5.0_21 1.6.0_21 1.7.0_06
WITH-SYNC (ms) 3190 614 565 587
WITHOUT-SYNC (ms) 3593 779 563 610
STRINGBUILDER (ms) N/A 450 434 475
Une fois aOgnacien ,a gwaiitnh, JwDOiKnt ch1e . J4aD,gK a ti1hn.e,4 ,cw oitdthehe wJciDotKdh e 1 t.wh4ie,t het xhttehe erc noeadxlet eswryinntachlh rtsohyneni czehexrdto enbrilnzoaecldk sbiylsno ccfhkar soitnsei rzf. ea dsH tobewlreo.vc ekHr o,iw sew vifetarhs, t JewDriK. t h5H oJawDneKdv e5ar b,ao nvwdei ,ta hbb ooJvtDehK, m5be otathnhod d msae btpohevoredf,so rbpmoe trehfq oumraemlt lheyoq duwsae llplley.r fwoerlml .eOqnucael layg awienl,l .avec JDK 1.4, le code avec le bloc synchronisé externe est plus rapide. Cependant, avec le JDK 5 et plus, les deux méthodes sont aussi performantes l'une que l'autre.
Voici ma classe test (n'hésitez pas à l'améliorer) :
public class StringBufferTest {
public static void unsync() {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 9999999; i++) {
buffer.append(i);
buffer.delete(0, buffer.length() - 1);
}
}
public static void sync() {
StringBuffer buffer = new StringBuffer();
synchronized (buffer) {
for (int i = 0; i < 9999999; i++) {
buffer.append(i);
buffer.delete(0, buffer.length() - 1);
}
}
}
public static void sb() {
StringBuilder buffer = new StringBuilder();
synchronized (buffer) {
for (int i = 0; i < 9999999; i++) {
buffer.append(i);
buffer.delete(0, buffer.length() - 1);
}
}
}
public static void main(String[] args) {
System.out.println(System.getProperty("java.version"));
// warm up
for(int i = 0 ; i < 10 ; i++){
unsync();
sync();
sb();
}
long start = System.currentTimeMillis();
unsync();
long end = System.currentTimeMillis();
long duration = end - start;
System.out.println("Unsync: " + duration);
start = System.currentTimeMillis();
sync();
end = System.currentTimeMillis();
duration = end - start;
System.out.println("sync: " + duration);
start = System.currentTimeMillis();
sb();
end = System.currentTimeMillis();
duration = end - start;
System.out.println("sb: " + duration);
}
}