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

Langage Java Discussion :

Fonctionnement de java en cas de concurrence d'accès


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2004
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 118
    Par défaut Fonctionnement de java en cas de concurrence d'accès
    Bonjour,

    j'écris ce post car je ne comprends pas très bien la problématique de concurrence d'accès.

    Voici un bean très simple :
    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
    import java.io.Serializable;
     
    public class MonBean implements Serializable {
    	/**
             * Sérialization.
             */
    	private static final long serialVersionUID = 1L;
     
    	private String maChaine = null; 
     
    	/**
             * Constructeur..
             */
    	public MonBean() {
    	}
     
    	public String getMaChaine() {
    		return this.maChaine;
    	}
     
    	public void setMaChaine(String maChaine) {
    		this.maChaine = maChaine;
    	}
    }
    J'ai réalisé les 3 tests suivants sans avoir d'erreur :
    • 2 threads qui font appel en même temps à la méthode getMaChaine() pendant 15 secondes
    • 1 thread qui lit sans cesse la méthode getMaChaine() pendant 15 secondes et un autre thread qui fait appel à la méthode setMaChaine() 5 secondes après son démarrage
    • 1 thread qui lit sans cesse la méthode getMaChaine() pendant 15 secondes et deux autres threads qui mettent à jour via la méthode setMaChaine() 5 secondes après leurs démarrages pendant 5 secondes...


    Je ne comprends pas, je n'obtient jamais d'Exception, pas de ConcurrentModificationException...

    Que doit-il se passer si deux threads accèdent en même temps à la méthode getMaChaine() ? Une exception doit-être soulevée ? Si aucune exception est levée, quel est l'intérêt de protéger la méthode avec synchronized ?

    Que doit-il se passer si deux threads accèdent en même temps à la méthode getMaChaine() pour l'un et setMaChaine() pour l'autre ? Une exception doit-être soulevée ? Si aucune exception est levée, on ne sait pas ce que va retourner getMaChaine()... C'est cela le problème ?

    Enfin, que doit-il se passe si deux threads accèdent en même temps à la méthode setMaChaine() ? Une exception doit-être soulevée ? On ne sait pas quel thread sera prioritaire ? C'est çà le problème ?

    Merci pour votre aide,
    F.

  2. #2
    Membre Expert
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Par défaut
    Attention que avec les threads l'expression en même temps est un peu piégée ; elle est quelque fois vrai, la plupart du temps fausse.

    À un thread donné, il faut quelques fois plusieurs opérations processeur pour mettre à jour une variable ; si, à ce moment, il est interrompu pour laisser l'initiative à un autre thread, alors la variable risque d'être laissée dans un état incohérent.

    Dans le cas des méthodes, cela ne pose jamais aucun problème : plusieurs threads peuvent déclencher en même temps la même méthode.

    Le problème se pose lorsque plusieurs threads veulent accéder à la même variable, et que l'un d'eux au moins modifie cette variable. C'est le cas pour ton maChaine. Mais, comme tu fais un simple changement de référence, il est difficile de le mettre en évidence. Dis-toi bien que ce genre de choses n'apparait que le jour de la livraison.

    Il est important de bien comprendre la théorie des threads, car leur agencement est difficile à tester, ils sont sources de bugs aléatoires, difficiles à reproduire et à résoudre.

    La ConcurentModificationException ne se produit que sur l'API des collections ; elle est levée suite à une vérif interne à cette api.

  3. #3
    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 gifffftane Voir le message
    Dans le cas des méthodes, cela ne pose jamais aucun problème : plusieurs threads peuvent déclencher en même temps la même méthode.
    Plus précisément, ce n'est pas l'appel de méthode qui est problématique, mais le code qu'elle exécute...

    Citation Envoyé par gifffftane Voir le message
    Le problème se pose lorsque plusieurs threads veulent accéder à la même variable, et que l'un d'eux au moins modifie cette variable. C'est le cas pour ton maChaine. Mais, comme tu fais un simple changement de référence, il est difficile de le mettre en évidence.
    Non ce code est tout à fait correct et thread-safe !!!

    Les affectations sont des opérations atomiques. C'est à dire qu'elles ne peuvent pas être interrompu par un autre traitement. Ainsi la référence a toujours une valeur correcte quoi qu'il arrive et cela ne pose aucun problème.

    Attention : ceci n'est pas forcément vrai avec les types long et double (cela dépend de la JVM) : Non-atomic Treatment of double and long


    Donc en fait tes références sont toujours "valides".




    En fait il y a 2 grands types de problèmes que tu peux rencontrés :

    1. En cas d'accès multiple à l'attribut, il est possible que ce dernier soit modifié entre temps, et donc que tu rentres dans un cas incohérent par rapport à ton code. Par exemple :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      	public String getMaChaineUpperCase() {
      		if (this.maChaine!=null) {
      				return this.maChaine.toUpperCase();
      		}
      		return "NULL";
      	}
      Ici malgré le fait que tu vérifies la nullité de l'attribut, tu peux avoir une NullPointerException si un autre thread fait un setMaChaine(null) entre ton if() et l'appel à toUpperCase()... ce qui est assez dur à mettre en évidence...

      Il y a une solution simple pour régler ce genre de problème : c'est d'utiliser une variable locale :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      	public String getMaChaineUpperCase() {
      		String maChaineLocal = this.maChaine;
      		if (maChaineLocal!=null) {
      				return this.maChaineLocal.toUpperCase();
      		}
      		return "NULL";
      	}
      Si ton attribut est modifié depuis un autre thread, tu utiliseras une valeur "obsolète" mais cela ne posera aucun problème

    2. Le second problème correspond à l'utilisation même des objets que tu manipules. Ici avec String aucun problème car elle est immuable ET thread-safe (cela va généralement ensemble). Mais si tu utilises un type muable ou non-thread-safe tu risques d'avoir des soucis très varié qu'il est difficile de prévoir...
      Dans ce cas là il y a diverses solutions (copie de protection, synchronisation, etc.) mais cela dépend vraiment de ton code et des objets que tu utilises.


    a++

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2004
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 118
    Par défaut
    Merci pour vos réponses à tous les deux.

    Mais que se passe-t-il lorsque deux threads tentent de lire ou d'écrire sur un objet de type StringBuilder (non thread-safe) ?
    Une exception va être soulevée ? Rien car l'affectation est une opération atomique ?

    >> Les affectations sont des opérations atomiques.
    Y compris les affectations de bean par exemple ?

    Comment savoir si un type est "thread-safe" ou pas ?
    Dans le package java.util.concurrent.atomic, on peut trouver des objets Integer, Long et Boolean sous forme atomique... cela les-rend-t-il thread-safe ?
    Qu'en est-il du type java.util.Date ?
    Faut-il utiliser le mot-clef volatile sur les objets non thread-safe pour forcer la mise à jour pour tous les threads ?

    Plus précisément, si je complète l'objet MonBean de la sorte :
    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
    import java.io.Serializable;
    import java.util.Date;
     
    public class MonBean implements Serializable {
    	/**
             * Sérialization.
             */
    	private static final long serialVersionUID = 1L;
     
    	private String maChaine = null; 
     
            private Date maDate = null;
     
    	/**
             * Constructeur..
             */
    	public MonBean() {
    	}
     
    	public String getMaChaine() {
    		return this.maChaine;
    	}
     
    	public void setMaChaine(String maChaine) {
    		this.maChaine = maChaine;
    	}
     
            public Date getMaDate() {
                   return this.maDate;
            }
     
            public void setMaDate(Date maDate) {
                   this.maDate = maDate;
            }
    }
    Que va-t-il se passer si deux threads mettent à jour maDate en même temps ? Si j'ai bien compris, pas de pbs car l'affectation est une opération atomique.
    Par contre, pour le thread qui va faire appel à getMaDate(), il y aura potentiellement un souci car entre deux opérations, la date aura pu être modifiée par un autre thread ?
    la solution n'est-t-elle pas de renvoyer une date clonée ?

    Merci pour votre aide,
    F.

  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
    Citation Envoyé par Foub Voir le message
    Mais que se passe-t-il lorsque deux threads tentent de lire ou d'écrire sur un objet de type StringBuilder (non thread-safe) ?
    Une exception va être soulevée ? Rien car l'affectation est une opération atomique ?
    L'affectation ne pose pas problème quel que soit le type de l'objet.
    C'est l'utilisation de l'objet en lui même qui posera problème.

    Par contre c'est très difficile de dire ce qui va arriver, car cela peut dépendre de beaucoup de chose dont l'implémentation et l'ordre d'exécution. Donc selon les cas :
    • Tout peut se passer très bien.
    • Tu peux avoir des exceptions diverses.
    • Tu peux avoir une exécution normale mais un résultat incorrect (élément tronqué, etc.)


    Citation Envoyé par Foub Voir le message
    >> Les affectations sont des opérations atomiques.
    Y compris les affectations de bean par exemple ?
    Un bean est un objet... donc OUI.

    Attention je parle uniquement des affectations avec "=" et non pas du code éventuelle qui peut entourer un getter/setter...

    Citation Envoyé par Foub Voir le message
    Comment savoir si un type est "thread-safe" ou pas ?
    C'est généralement indiqué dans la doc... sachant que les types de bases sont immuable et donc typesafe (String, Integer, Long, Double, etc...)
    Citation Envoyé par Foub Voir le message
    Dans le package java.util.concurrent.atomic, on peut trouver des objets Integer, Long et Boolean sous forme atomique... cela les-rend-t-il thread-safe ?
    Oui

    Citation Envoyé par Foub Voir le message
    Qu'en est-il du type java.util.Date ?
    Elle n'est pas immuable, et rien n'est indiqué dans la doc... donc on doit la considéré comme non thread-safe !

    Citation Envoyé par Foub Voir le message
    Faut-il utiliser le mot-clef volatile sur les objets non thread-safe pour forcer la mise à jour pour tous les threads ?
    Que l'objet soit thread-safe ou pas n'a rien à voir avec volatile. Ce mot clef doit être utilisé pour tous les attributs qui peuvent être modifié par différents threads, afin d'interdire à la JVM d'optimiser l'accès à sa valeur.

    Si on ne fait pas cela la JVM pourrait complètement ignorer ses modifications depuis un autre thread...

    Citation Envoyé par Foub Voir le message
    Plus précisément, si je complète l'objet MonBean de la sorte :
    Déjà, sans parler de multi-threading, ton beans est incorrect puisqu'on peut très bien faire cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Date date = new Date(0L);
    monBean.setMaDate(date);
    date.setTime(1000L); // Je modifie la date sans passer par le setter !!!
    Citation Envoyé par Foub Voir le message
    Que va-t-il se passer si deux threads mettent à jour maDate en même temps ? Si j'ai bien compris, pas de pbs car l'affectation est une opération atomique.
    En effet... Par contre les modifs depuis l'extérieur sur ton objet Date pourrait poser des problèmes, puisque ce dernier n'est pas immuable...

    Citation Envoyé par Foub Voir le message
    la solution n'est-t-elle pas de renvoyer une date clonée ?
    Oui. Mais ici ce n'est pas particulièrement un problème de multi-threading) : tous les attributs non-immuable ne devrait pas être exporté directement : il faut impérativement faire des copies de sécurité dans le getter/setter :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            public Date getMaDate() {
                   return new Date(this.maDate.getTime());
            }
     
            public void setMaDate(Date maDate) {
                   this.maDate = new Date(maDate.getTime());;
            }

    a++

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2004
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 118
    Par défaut
    Merci beaucoup.
    C'est très clair

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

Discussions similaires

  1. [Port série] [QExtSerialPort] write() ne fonctionne que dans certains cas
    Par sybe30 dans le forum Bibliothèques
    Réponses: 1
    Dernier message: 14/12/2012, 19h27
  2. Classement qui ne fonctionne pas dans certains cas
    Par Furious68 dans le forum Requêtes
    Réponses: 4
    Dernier message: 19/05/2010, 16h47
  3. Réponses: 1
    Dernier message: 10/12/2009, 22h26
  4. Tétraèdrisation ne fonctionne que dans certains cas
    Par Rafy dans le forum Algorithmes et structures de données
    Réponses: 8
    Dernier message: 15/12/2008, 20h51

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