IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Concurrence et multi-thread Java Discussion :

[MultiThreading] DecimalFormat.parse non thread-safe


Sujet :

Concurrence et multi-thread Java

  1. #1
    Membre émérite Avatar de Jidefix
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    742
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations forums :
    Inscription : Septembre 2006
    Messages : 742
    Par défaut [MultiThreading] DecimalFormat.parse non thread-safe
    Bonjour,

    j'ai un petit problème avec le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
    public class Test {	
    	public static void main(String[] args) {
    		MonThread t1 = new MonThread("t1");
    		MonThread t2 = new MonThread("t2");
    		MonThread t3 = new MonThread("t3");
    		MonThread t4 = new MonThread("t4");
    		MonThread t5 = new MonThread("t5");
    		MonThread t6 = new MonThread("t6");
    		t2.start();
    		t1.start();
    		t3.start();
    		t4.start();
    		t5.start();
    		t6.start();
    	}
    }
     
    class MonThread extends Thread{
    	static TimeZone Tz_UTC = TimeZone.getTimeZone("UTC");
    	static DecimalFormat dfFlowID = new DecimalFormat ("00000");
    	static Object o=new Object();
     
    	public MonThread(String name){
    		super(name);
    	}
    	public void run(){
    		try{
    			for(int i=0; i<20000; i++){
    				synchronized(o){
    				    long l = dfFlowID.parse("123").longValue();
    			    }
    			}
    			System.out.println(this.getName()+" fini");
    		}
    		catch(Exception e){
    			e.printStackTrace();
    		}
     
    	}
    }

    En gros, le thread va parser "123" 20000 fois via le DecimalFormat.parse(), et je lance 6 thread en même temps.

    Résultat avec le synchronized(o):
    les 6 threads se terminent correctement

    Résultat en retirant le synchronized(o):
    Les threads se plantent aléatoirement sur des exceptions variées:
    "java.lang.NumberFormatException: empty String"
    "java.lang.NumberFormatException: For input string: "E.3121323""
    "java.lang.NumberFormatException: For input string: """
    Et autres "multiple points" ...


    En gros si je comprends bien la méthode DecimalFormat.parse doit être synchronisée, mais why? Il me semblait qu'il ne fallait synchroniser que les blocs où l'on modifie une variable commune, or ici la seule variable commune est l'instance de DecimalFormat.
    Cette instance serait modifiée par le "parse" ?

  2. #2
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    d'une manière générale, tout méthode de l'api qui ne mentionne pas explicitement qu'elle est threadsafe ne dois pas être considérée comme telle.

    En l'occurence, dans ce cas ci, decimal format peut être utilisé pour faire du parsing progressif d'une chaine (voir toutes les méthodes relative à ca qu'exporte DecimalFormat). Donc,oui, decimal format a un état et cet état est modifié durant le parse().

    Pour plus de détails, voir la source de decimal format.

  3. #3
    Membre émérite Avatar de Jidefix
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    742
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations forums :
    Inscription : Septembre 2006
    Messages : 742
    Par défaut
    Ok, à l'avenir je ferai plus attention avec les variables partagées par les threads, c'est vrai que si on connait pas l'implémentation, un simple toString() peut devenir piégeux...

  4. #4
    Membre émérite Avatar de Jidefix
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    742
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations forums :
    Inscription : Septembre 2006
    Messages : 742
    Par défaut
    Oh: question toute bête:
    D'un point de vue performance, il serait plus sage de déclarer le DecimalFormat en variable d'instance (lourdeur de création de DecimalFormat) ou de synchroniser l'appel?
    Je précise qu'en vrai, ce bloc de code est inclut dans un service qui peut s'exécuter plusieurs centaines de fois en même temps sur notre serveur...

  5. #5
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,

    Citation Envoyé par tchize_ Voir le message
    d'une manière générale, tout méthode de l'api qui ne mentionne pas explicitement qu'elle est threadsafe ne dois pas être considérée comme telle.
    +1

    D'autant plus que cela est bien souvent renseigné dans la javadoc.
    Par exemple pour DateFormat : http://javasearch.developpez.com/j2s...ynchronization
    Synchronization

    Decimal formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.




    Citation Envoyé par Jidefix Voir le message
    D'un point de vue performance, il serait plus sage de déclarer le DecimalFormat en variable d'instance (lourdeur de création de DecimalFormat) ou de synchroniser l'appel?
    Je précise qu'en vrai, ce bloc de code est inclut dans un service qui peut s'exécuter plusieurs centaines de fois en même temps sur notre serveur...
    Cela dépend beaucoup de ton architecture et de ce que tu entends par "performance".

    La duplication des DecimalFormat par thread reste à mon avis la solution la plus simple qui ne devrait pas trop impacter les performances, avec un léger impact sur la consommation mémoire tant que tu les limites bien à 1 par thread (tu peux éventuellement utiliser un ThreadLocal, même si une variable locale pourrait être préférable selon ton besoin concret).

    La synchronisation permet de limiter la consommation mémoire, mais pourra provoquer des lenteurs, en particulier sur une machine multi-process/multi-coeur...


    a++

    PS : C'est normal que tu utilises un DecimalFormat pour lire un nombre ? Autant utiliser directement Long.parseLong() dans ce cas !

  6. #6
    Membre émérite Avatar de Jidefix
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    742
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations forums :
    Inscription : Septembre 2006
    Messages : 742
    Par défaut
    A priori il s'agit d'une architecture mono-coeur (mais ça pourrait évoluer à l'avenir)
    Par contre on est très light niveau mémoire, c'est pour ça que ça ne semble pas illogique de faire appel à la même variable...
    Disons que la il s'agit de faire évoluer un service déjà utilisé en production, donc je préférerai limiter au maximum les modifications

    Pour le DecimalFormat, en fait le but final est de formater un entier récupéré sous forme de String afin de l'avoir sur 5 chiffre exactement, donc je suis obligé de passer par un DecimalFormat pour le formatage (en revanche je peux très bien utiliser Integer.parseInt() pour le parsing du nombre effectivement )

    (en fait si je faisais ce que je voulais je parserai juste l'entier et je rajouterai des zéros à gauche mais bon...)

    Merci pour vos conseils!

  7. #7
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Jidefix Voir le message
    Par contre on est très light niveau mémoire, c'est pour ça que ça ne semble pas illogique de faire appel à la même variable...
    Si light que cela que tu ne peux pas créer quelques DecimalFormat !


    Citation Envoyé par Jidefix Voir le message
    Pour le DecimalFormat, en fait le but final est de formater un entier récupéré sous forme de String afin de l'avoir sur 5 chiffre exactement, donc je suis obligé de passer par un DecimalFormat pour le formatage (en revanche je peux très bien utiliser Integer.parseInt() pour le parsing du nombre effectivement )
    Oublies mon PS : je parlais de DecimalFormat mais j'avais en tête DateFormat d'où mon incompréhension ! Il est 15h30 et je ne suis pas encore bien réveillé moi

    Citation Envoyé par Jidefix Voir le message
    (en fait si je faisais ce que je voulais je parserai juste l'entier et je rajouterai des zéros à gauche mais bon...)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    String number = String.format("%05d", Integer.parseInt("123"));
    (par contre je n'ai aucune idée de ce que ca donne au niveau performance en comparaison d'un DecimalFormat).

    a++

  8. #8
    Membre émérite Avatar de Jidefix
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    742
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations forums :
    Inscription : Septembre 2006
    Messages : 742
    Par défaut
    Ben il nous est dejà arrivé d'avoir des outOfMemory quand même... En général on rajoute de la mémoire à la JVM et/ou à la machine, mais là on est plutôt dans une politique de rationnement de la mémoire (ya pas de petits profits!)
    Enfin c'est sur qu'on a des problèmes de conception à corriger, mais la liste serait longue :'(

  9. #9
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    un decimalformat doit occuper à tout casser 100 octest, sur 5000 thread, ca donnerais à tout casser 0.5M de mémoire occupée. Et si t'as 5000 threads tu tourne surement déjà sur 512M dans la JVM

    ok si tu devias faire tourner ton code dnas des jvm de 200k mais là

    En plus si la mémoire est un problème, vu l'impact du synchronized, il vaut peut etre alors mieux recréer le décimal format à la demande et le jeter juste après, tu perdra, je pense, pas plus de temps qu'à mettre tes threads à la queue l'un de l'autre.

  10. #10
    Membre émérite Avatar de Jidefix
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    742
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations forums :
    Inscription : Septembre 2006
    Messages : 742
    Par défaut
    C'est pas faux... bon je vais tester les deux et voir si ça fait une différence notable alors

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Web Service / SQL / Multithread / thread-safe
    Par neoncyber dans le forum Développement Web avec .NET
    Réponses: 6
    Dernier message: 18/11/2011, 12h04
  2. Code non thread-safe
    Par khazna dans le forum C++
    Réponses: 9
    Dernier message: 06/03/2008, 17h28
  3. [RCP] Treeviewer non thread-safe ?
    Par Guildux dans le forum Eclipse Platform
    Réponses: 4
    Dernier message: 09/01/2007, 13h00
  4. Code "Thread Safe" ?
    Par Neitsa dans le forum C++
    Réponses: 3
    Dernier message: 23/12/2005, 14h33
  5. [MFC] CMAP non thread safe ?
    Par fmarot dans le forum MFC
    Réponses: 5
    Dernier message: 04/10/2005, 13h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo