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 :

[Debutant] Pb de synchronisation de Threads


Sujet :

Concurrence et multi-thread Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre du Club
    Inscrit en
    Octobre 2006
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Octobre 2006
    Messages : 8
    Par défaut [Debutant] Pb de synchronisation de Threads
    Bonjour,

    Je démarre en Java avec le bouquin de Claude Delannoy.
    J'en suis à la synchronisation des Threads avec la pose de verrous par synchronized.

    Et la ca dérape

    Pour bien comprendre, je réalise un petit programme de test :
    Le main crée 2 objets runnable, les instancie, et les démarre.
    Chaque run a une boucle de 0 à 1000 et appelle une methode synchronized qui icrémente et lit un champs compteur static.
    Toujours dans la boucle, chaque thread affiche sur la console le nom du thread et la valeur du compteur.

    Arrivé à la fin le compteur ne contient pas 2000 (ie 2 fois les 1000 de chaque thread) mais un peu moins.
    Comme si le verrou n'avait pas fonctionné.

    J'ai tourné et retourné le pb et je n'ai trouvé qu'une seule solution qui fonctionne:
    C'est que la méthode synchronisée qui incrémente le compteur soit déclarée static.

    Comme je ne trouve nulle part dans les forums et dans les FAQs ce genre de solution,
    je me demande si je ne me suis pas fourvoyé ailleurs

    Avez vous une idée ?
    Gérard

    PS j'utilise le JSE 1.5.0_06-b05 avec Eclipse 3.2.0
    sur une machine Windows XP processeur intel dual-core (est-ce que ca a une importance ?)

    je vous joins le code:
    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
     
    class TestThread implements Runnable {
    	public TestThread(String name ){
    		this.name = name;
    	}
     
    	synchronized static int incCompteur(){
    		return  ++compteur;
    	}
     
    	private static Integer compteur = 0;
    	private String name;
     
     
    	public void run( ) {
    			int c;
    			for( int i = 0; i < 1000; i++ ){
    				c = incCompteur();
    				Thread.yield();
    				System.out.println( name + " : " + c);
    			}
    			System.out.println( name + " fin : " + compteur);
    	}
    }
     
     
    public class TestThr4 {
    	public static void main(String[] args) {
     
    		TestThread tt1 = new TestThread("A");
    		TestThread tt2 = new TestThread("B");
    		Thread t1 = new Thread( tt1 );
    		Thread t2 = new Thread( tt2 );
    		t1.start();
    		t2.start();
    	}
    }

  2. #2
    Membre chevronné Avatar de billynirvana
    Homme Profil pro
    Architecte technique
    Inscrit en
    Décembre 2004
    Messages
    472
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Architecte technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2004
    Messages : 472
    Par défaut
    Bonjour, Dans le cas où tu n'obtiens pas 2000, quelle valeur est affichée?

    Pour la synchronisation des threads, rien de tel que de commencer avec le problème du pont à une voie:

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    // Ce programme gere le pont à une voie.
    // Une voiture ne s'engage seulement si aucune voiture n'est en face
     
     
    class Pont
    {
    	private int e = 0;
     
    	// procedure d'entree sur le pont pour une voiture qui arrive de l'est
    	public synchronized void entreeEst()
    	{
    		try
    		{
    			while(e < 0)
    				wait();
    		}
    		catch (Exception e)
    		{
    			System.out.println("Erreur!");
    			e.printStackTrace();
    		}
     
    		e++;
    	}
     
    	// procedure d'entree sur le pont pour une voiture qui arrive de l'ouest
    	public synchronized void entreeOuest()
    	{
    		try
    		{
    			while(e > 0)
    				wait();
    		}
    		catch (Exception e)
    		{
    			System.out.println("Erreur!");
    			e.printStackTrace();
    		}
     
    		e--;
    	}
     
    	// procedure de sortie sur le pont pour une voiture qui arrive de l'est
    	public synchronized void sortieEst()
    	{
    		e--;
    		notifyAll();
    	}
     
    	// procedure de sortie sur le pont pour une voiture qui arrive de l'ouest
    	public synchronized void sortieOuest()
    	{
    		e++;
    		notifyAll();
    	}
    }
     
     
    class VoitureOuest extends Thread
    {
    	int num;
    	Pont pont;
     
    	// constructeur
    	VoitureOuest(Pont pont, int num)
    	{
    		this.pont = pont;
    		this.num = num;
    	}
     
    	// Execution du processus ici
    	public void run()
    	{
    			// Avancer()
    			System.out.println("VoitureOuest numero " + num + " arrive sur le pont.");
     
    			pont.entreeOuest();
     
    			// Traverser()
    			System.out.println("VoitureOuest numero " + num + " est sur le pont.");
     
    			for(int k=0; k < 5000000; k++) {}
     
    			// Partir()
    			System.out.println("VoitureOuest numero " + num + " s'eloigne du pont.");
    			pont.sortieOuest();
    	}
    }
     
     
    class VoitureEst extends Thread
    {
    	int num;
    	Pont pont;
     
    	// constructeur
    	VoitureEst(Pont pont, int num)
    	{
    		this.pont = pont;
    		this.num = num;
    	}
     
    	// Execution du processus ici
    	public void run()
    	{
    			// Avancer()
    			System.out.println("VoitureEst numero " + num + " arrive sur le pont.");
     
    			pont.entreeEst();
     
    			// Traverser()
    			System.out.println("VoitureEst numero " + num + " est sur le pont.");
     
    			for(int k=0; k < 5000000; k++) {}
     
    			// Partir()
    			System.out.println("VoitureEst numero " + num + " s'eloigne du pont.");
    			pont.sortieEst();
    	}
    }
     
     
    public class Pontver1
    {
    	// le moniteur
    	static Pont pont =  new Pont();
     
    	public static void main(String [] args)
    	{
    		// Creation des voitures (au nombre de 30)
    		for (int i = 0 ; i < 30; i++)
    		{
    			double r = Math.random();
     
    			// on cree une voiture a une frequence differente (entre 0 et 100000 *r tour de boucle)
    			for(int k=0; k < 1000000*r; k++) {}
     
    			// une chance sur deux que la voiture vient de l'ouest (sinon vient de l'est)
    			if (r < .5)
    				(new VoitureOuest(pont, i+1)).start();
    			else
    				(new VoitureEst(pont, i+1)).start();
    		}
    	}
    }

  3. #3
    Membre éprouvé Avatar de BlackWood
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    167
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 167
    Par défaut
    Bonjour Gérard,

    Bienvenue sur les forums de Developpez.com, et merci pour ton premier message propre et précis.

    C'est curieux car j'ai testé ton code et il me retourne bien 2000.
    Il serait intéressant de connaître l'origine du problème. Pour l'instant, je ne vois pas, la seule hypothèse serait le dual core (?). Mais pour quelle raison n'exécuterait-il pas correctement les Thread ?

    A vérifier.

    Edit : grilled et à coté de la plaque : j'ai testé avec le static...

  4. #4
    Membre Expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Par défaut
    Je te propose mon article paru récemment:
    Java et la synchronisation

    Tu trouveras peut-être des réponses à tes questions

  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,



    Le mot clef synchronized permet de poser un verrou sur une référence. Pour une même référence on ne peut poser qu'un seul verrou à la fois.

    Les méthodes d'instances sont synchronisées en utilisant l'intance courante (this) alors que les méthodes static le sont en utilisant le type représentant la classe.

    Ainsi le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    synchronized static int incCompteur(){
            return  ++compteur;
        }
    Est en fait équivalent à ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    static  int incCompteur(){
            synchronized(TestThread.class) {
                    return  ++compteur;
            }
        }
    Comme TestThread est unique les opérations synchronisé se déroule correctement sur le même verrou.


    Maintenant si la méthode n'est pas static :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    synchronized int incCompteur(){
            return  ++compteur;
        }
    Est en fait équivalent à ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int incCompteur(){
            synchronized(this) {
                    return  ++compteur;
            }
        }
    Or comme tu as deux instances différentes, tu utilises deux verroux différents et donc il n'y a pas de synchronisation (en gros chaque thread a son propre verrou).


    Donc si tu manipules un élément static, tu dois utiliser un verrou static/constant. Et si tu manipules un attribut d'instance, tu peux utiliser un verrour d'instance...

    a++

    PS : Il serait préférable d'utiliser un int plutôt qu'un Integer afin d'éviter la création d'objet temporaire à chaque modification...

  6. #6
    Membre du Club
    Inscrit en
    Octobre 2006
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Octobre 2006
    Messages : 8
    Par défaut
    Merci pour vos réponses (je ne m'attendais pas à une telle réactivité).

    billynirvana:
    Quand je n'obtiens pas 2000 j'obtiens des résultats variables entre 1993 et 1998.
    Et merci pour l'exemple du pont à une voie

    adiGuba:
    Je ne comprends pas ce que signifie synchronized(TestThread.class)
    Mais bon voyons la suite.

    BlackWood:
    Je viens d'installer Eclipse sur mon autre machine:
    Pentium4 toujours windows XP
    Pour etre un peu plus sur j'ai augmente la boucle à 1 million.

    Avec l'Hyperthreading activé dans le bios:
    - Sans static : meme probleme qu'avec le dual-core mais la frequence d'apparition est moindre. Le compteur final oscille entre 1999850 et 1999650.
    - Sans le synchronized de la methode incCompteur le resultat est le meme.
    - Avec synchronized static on obtient bien 2000000.

    En desactivant l'hyperthreading dans le bios:
    - avec synchronized mais sans static :aucun probleme, le compteur de fin de boucle affiche bien 2000000 de maniere repetitive.
    - sans synchronized et sans static : aucun probleme non plus. Comme s'il n'y avait pas necessité de poser des verrous. ??????

    Il y a certainement une explication ... mais je ne la connais pas.

  7. #7
    Membre éprouvé Avatar de BlackWood
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    167
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 167
    Par défaut
    Définitions de Hyperthreading sur le Web :
    C'est une technologie qui équipe les Pentium4 Cd'Intel qui leurre Windows en lui faisant croire qu'il ya 2 processeurs.
    Hahaaa ! Le dual core doit donc bien y être pour quelque chose... Ca c'est curieux.

  8. #8
    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 aPique
    adiGuba:
    Je ne comprends pas ce que signifie synchronized(TestThread.class)
    Mais bon voyons la suite.
    TestThread.class est l'objet de type Class qui représente ton type TestThread. Il est unique pendant toute l'application.

    Ainsi lorsque tu synchronises une méthodes static, tous les appels à la méthode sont synchronisé via le même lock. Il y a une seule porte d'entrée et donc pas de problème.


    Lorsque tu utilises une méthode d'instance, la synchronisation se fait sur deux instances distinctes, et donc tu as deux locks distincts chaque thread utilise sa propre porte et donc il n'y a pas de synchronisation !!!


    a++

  9. #9
    Membre du Club
    Inscrit en
    Octobre 2006
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Octobre 2006
    Messages : 8
    Par défaut
    Voila ce que je comprends de tes explications:
    TestThread.class representerait le code machine qui s'execute alors que les instances de classe representeraient les datas associées.

    Si je fais le lien avec mon probleme, dans le cas d'une machine multi-processeurs, il y aurait duplication du code dans de la memoire dediee à chaque processeur sauf si l'on precise que la classe doit etre static, auquel cas le code serait mis dans de la memoire partagée entre les processeurs.

  10. #10
    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 aPique
    Voila ce que je comprends de tes explications:
    TestThread.class representerait le code machine qui s'execute alors que les instances de classe representeraient les datas associées.
    Non !

    TestThread.class est un objet en mémoire mais il est UNIQUE. Il est utilisé par synchronized comme verrou lorsque tu synchronizes une méthode statique. Ainsi comme tu utilises le même verrou dans les deux threads il n'y a pas de problème de synchronisation.


    Si la méthode n'est pas static, la synchronisation se fait sur l'instance de l'objet. Or dans ton cas tu as deux objets TestThread : tt1 et tt2.
    Donc tu utilises deux verrous : un par thread !!! Et du coup IL N'Y A PLUS DE SYNCHRONISATION !

    Donc le problème ne vient pas de ton processeur dual core, mais du fait que tu as une mauvaise synchronisation...

    a++





    Si je fais le lien avec mon probleme, dans le cas d'une machine multi-processeurs, il y aurait duplication du code dans de la memoire dediee à chaque processeur sauf si l'on precise que la classe doit etre static, auquel cas le code serait mis dans de la memoire partagée entre les processeurs.[/quote]

  11. #11
    Membre du Club
    Inscrit en
    Octobre 2006
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Octobre 2006
    Messages : 8
    Par défaut
    Oui, pourquoi pas.

    Mais alors comment déclarer deux verrous différents dans la meme classe puisqu'en fait c'est la classe elle meme ou du moins TestThread.class qui sert de verrou ?

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

Discussions similaires

  1. Question sur la synchronisation des threads.
    Par sebastieng dans le forum Langages de programmation
    Réponses: 4
    Dernier message: 07/12/2005, 15h55
  2. [Debutant] Faut-il tuer les Threads Static?
    Par sniperseb dans le forum MFC
    Réponses: 5
    Dernier message: 05/12/2005, 14h43
  3. Réponses: 1
    Dernier message: 23/05/2005, 15h52
  4. [débutant]Etats de synchronisation
    Par Invité dans le forum Autres Diagrammes
    Réponses: 5
    Dernier message: 30/08/2004, 12h34
  5. Synchronisation de thread
    Par declencher dans le forum Langage
    Réponses: 2
    Dernier message: 07/01/2004, 10h28

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