Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
929 connectés 

  FORUM HardWare.fr
  Programmation
  Java

  [NEW QUESTION] problème de concurrence et de verouillage en Java

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

[NEW QUESTION] problème de concurrence et de verouillage en Java

n°571629
Roco
Posté le 20-11-2003 à 15:00:03  profilanswer
 

Le but de ma démarche est de démontrer que si je ne verouille pas certains objets et que ceux-ci sont lu et modifié par différents processus, au bout, d'un certain temps, ces objets deviendront corrompus.
 
Mon exemple est celui d'un système bancaire avec un agent qui passe des transactions dans différentes banques sur différents comptes clients. Il fait des virement (débit + crédit).
 
Problème n°1 (technique) : je n'arrive pas à lancer les processus en parallèles. Malgré l'insertion de sleep et de yeild dans les threads, ceux-ci se déroulent séquentiellement...
 
Problème n°2 (conception): Je ne pense pas que ma modélisation soit correcte pour démontrer mon objectif. Et je crois que je devrais gérer cela un niveau plus bas (un objet en-dessous).
 
Voici la classe principale :
 

Code :
  1. public class Main {
  2. public static void main(String[] args) {
  3.  Compte compteCh = new Compte(1000);
  4.  Compte compteCj = new Compte(1000);
  5.  Client clientCh = new Client("ch", compteCh);
  6.  Client clientCj = new Client("cj", compteCj);
  7.  Banque banqueBk = new Banque();
  8.  banqueBk.ajouterClient(clientCh);
  9.  Banque banqueBl = new Banque();
  10.  banqueBl.ajouterClient(clientCj);
  11.  Agent a1 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 1);
  12.  Agent a2 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 2);
  13.  Agent a3 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 3);
  14.  Agent a4 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 4);
  15.  Agent a5 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 5);
  16.  Agent a6 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 6);
  17.  Agent a7 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 7);
  18.  Agent a8 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 8);
  19.  Agent a9 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 9);
  20.  a1.run();
  21.  a2.run();
  22.  a3.run();
  23.  a4.run();
  24.  a5.run();
  25.  a6.run();
  26.  a7.run();
  27.  a8.run();
  28.  a9.run();
  29.  System.out.println(" Solde du client ch : " + compteCh.getSolde());
  30.  System.out.println(" Solde du client cj : " + compteCj.getSolde());
  31. }
  32. }


 
Voici la classe Agent :
 

Code :
  1. public class Agent implements Runnable {
  2. int numero;
  3. Banque b1;
  4. Banque b2;
  5. Client c1;
  6. Client c2;
  7. public Agent(Banque b1, Banque b2, Client c1, Client c2, int numero) {
  8.  this.numero = numero;
  9.  this.b1 = b1;
  10.  this.b2 = b2;
  11.  this.c1 = c1;
  12.  this.c2 = c2;
  13. }
  14. public void passerTransaction(Client c1, Client c2, Banque b1, Banque b2, double somme) {
  15.  b1.debiterClient(c1, somme);
  16.  b2.crediterClient(c2, somme);
  17.  System.out.println("Transaction effectuee sur le client " + c1.getNom() + " par la processus " + numero);
  18. }
  19. public void run() {
  20.  passerTransaction(c1, c2, b1, b2, 500);
  21.  try {
  22.   Thread.sleep((int)(Math.random()*1000));
  23.  }
  24.  catch (InterruptedException e) {}
  25.  Thread.yield();
  26.  passerTransaction(c2, c1, b2, b1, 500);
  27.  try {
  28.   Thread.sleep((int)(Math.random()*1000));
  29.  }
  30.  catch (InterruptedException e) {}
  31.  Thread.yield();
  32.  passerTransaction(c1, c2, b1, b2, 500);
  33.  try {
  34.   Thread.sleep((int)(Math.random()*1000));
  35.  }
  36.  catch (InterruptedException e) {}
  37.  Thread.yield();
  38.  passerTransaction(c2, c1, b2, b1, 500);
  39.  try {
  40.   Thread.sleep((int)(Math.random()*1000));
  41.  }
  42.  catch (InterruptedException e) {}
  43.  passerTransaction(c1, c2, b1, b2, 500);
  44.  try {
  45.   Thread.sleep((int)(Math.random()*1000));
  46.  }
  47.  catch (InterruptedException e) {}
  48.  passerTransaction(c2, c1, b2, b1, 500);
  49.  try {
  50.   Thread.sleep((int)(Math.random()*1000));
  51.  }
  52.  catch (InterruptedException e) {}
  53.  passerTransaction(c1, c2, b1, b2, 500);
  54.  try {
  55.   Thread.sleep((int)(Math.random()*1000));
  56.  }
  57.  catch (InterruptedException e) {}
  58.  passerTransaction(c2, c1, b2, b1, 500);
  59. }
  60. }


 
Voici la classe Banque :
 

Code :
  1. import java.util.*;
  2. public class Banque implements Runnable {
  3. Vector lesClient;
  4. public Banque() {
  5.  lesClient = new Vector();
  6. }
  7. public void run() {
  8.  while (true) {
  9.  }
  10. }
  11. public void ajouterClient(Client client) {
  12.  lesClient.add(client);
  13. }
  14. public Client rechercheClient(Client client) {
  15.  Client clientRenvoye = null;
  16.  Enumeration en = lesClient.elements();
  17.  while (en.hasMoreElements()) {
  18.   Client clientTampon = (Client) en.nextElement();
  19.   if (clientTampon.getNom() == client.getNom()) {
  20.    clientRenvoye = clientTampon;
  21.   }
  22.  }
  23.  return clientRenvoye;
  24. }
  25. public void crediterClient(Client client, double somme) {
  26.  Client c = rechercheClient(client);
  27.  double soldeAnterieur = c.getCompte().getSolde();
  28.  double soldePosterieur = soldeAnterieur + somme;
  29.  c.getCompte().setSolde(soldePosterieur);
  30. }
  31. public void debiterClient(Client client, double somme) {
  32.  Client c = rechercheClient(client);
  33.  double soldeAnterieur = c.getCompte().getSolde();
  34.  double soldePosterieur = soldeAnterieur - somme;
  35.  c.getCompte().setSolde(soldePosterieur);
  36. }
  37. }


 
Voici la classe Client :
 

Code :
  1. public class Client {
  2. String nom;
  3. Compte compte;
  4. public Client(String nom, Compte compte) {
  5.  this.nom = nom;
  6.  this.compte = compte;
  7. }
  8. public String getNom() {
  9.  return nom;
  10. }
  11. public void setNom(String nom) {
  12.  this.nom = nom;
  13. }
  14. public Compte getCompte() {
  15.  return compte;
  16. }
  17. public void setCompte(Compte compte) {
  18.  this.compte = compte;
  19. }
  20. }


 
Et enfin, voici la classe Compte :
 

Code :
  1. public class Compte {
  2. double solde;
  3. public Compte(double solde) {
  4.  this.solde = solde;
  5. }
  6. public double getSolde() {
  7.  return solde;
  8. }
  9. public void setSolde(double solde) {
  10.  this.solde = solde;
  11. }
  12. }


 
Mon but final est d'appliquer des verrous exlusifs sur le comptes et des verrous partagés sur les banques.
 
Mais d'abord, je voudrais voir l'erreur tourner, sinon je ne pourrais jamais prendre réellement conscience du problème.
 
Merci d'avance :hello:


Message édité par Roco le 20-11-2003 à 16:04:14
mood
Publicité
Posté le 20-11-2003 à 15:00:03  profilanswer
 

n°571631
benou
Posté le 20-11-2003 à 15:04:20  profilanswer
 

en lisant rapidement, je m'aperçoit que tu ne créé aucun thread ... donc forcément, ton prg est séquantiel :  
remlpace :

Code :
  1. a1.run();
  2.         a2.run();
  3.         a3.run();
  4.         a4.run();
  5.         a5.run();
  6.         a6.run();
  7.         a7.run();
  8.         a8.run();
  9.         a9.run();


 
par  

Code :
  1. new Thread(a1).start();
  2.         new Thread(a2).start();
  3.         new Thread(a3).start();
  4.         new Thread(a4).start();
  5.         new Thread(a5).start();
  6.         new Thread(a6).start();
  7.         new Thread(a7).start();
  8.         new Thread(a8).start();
  9.         new Thread(a9).start();


Message édité par benou le 20-11-2003 à 15:04:39

---------------
ma vie, mon oeuvre - HomePlayer
n°571632
lorill
Posté le 20-11-2003 à 15:04:55  profilanswer
 

benou a écrit :

en lisant rapidement, je m'aperçoit que tu ne créé aucun thread ... donc forcément, ton prg est séquantiel :  
remlpace :

Code :
  1. a1.run();
  2. ...


 
par  

Code :
  1. new Thread(a1).run();
  2. ...




 
.start() benou, .start() ! :o
 
Edit : [:absynthe]


Message édité par lorill le 20-11-2003 à 15:07:05
n°571636
benou
Posté le 20-11-2003 à 15:06:04  profilanswer
 

lorill a écrit :

.start() benou, .start() ! :o


t'aurais du me quoter  :kaola:


---------------
ma vie, mon oeuvre - HomePlayer
n°571637
lorill
Posté le 20-11-2003 à 15:07:19  profilanswer
 

benou a écrit :


t'aurais du me quoter  :kaola:  

[:absynthe]

n°571642
Roco
Posté le 20-11-2003 à 15:11:43  profilanswer
 

Ha vi chui un peu con là !
 
Merci bcp.

n°571647
Rawhead re​x
Argghhhhh
Posté le 20-11-2003 à 15:19:08  profilanswer
 

Si j'ai bien compris, tu pourrais essayer de mettre cela en evidence, en violant une condition de causalité. Admettons que tu veuilles que la transaction soit uniquement possible si cette operation laisse les 2 comptes positifs (je sais que les banques autorisent les decouverts mais bon ;) ).  
 
Reprenons ta fonction transaction et modifions la
 
public void passerTransaction(Client c1, Client c2, Banque b1, Banque b2, double somme) {  
 rajout du test  : if (c1-somme>0){
 b1.debiterClient(c1, somme);  
 b2.crediterClient(c2, somme);  
 System.out.println("Transaction effectuee sur le client " + c1.getNom() + " par la processus " + numero);  
}
else  
{
System.out.println("transaction impossible" ) ;
}
}  
 
Notons T1 et T2 2 transactions, si elles sont successives, il n'y aura pas de problemes puisque la condition de test sera toujours efficace. Si maintenant ces 2 transactions ont lieu en meme temps (ou quasiment) et que les 2 threads sont tels que :
T1 a reussi le test et s'apprete a executer le code  
T2 a finie et est en train d'actualiser le compte en banque.
 
Alors quand T1 va executer la transaction, la clause de positivité du résultat ne sera plus valide, puisqu'entre temps T2 aura diminuer le compte (par exemple). Tu pourrais donc te retrouver avec un compte negatif, alors que tu avais bien specifie dans ton code le contraire...
 
Voila j'espere que ca as pu t'aider ;)

n°571686
Roco
Posté le 20-11-2003 à 16:03:33  profilanswer
 

C'est un peu cela...
 
En fait, je voudrais démontrer que si je ne verrouille (de façon exclusive) pas le compte en banque. Je risque d'avoir des incohérences à la fin du programme.
 
Ex :
 
P1       P2
LIT(X)
X+1000
         LIT(X)
         X+2000
ECRIT(X)
         ECRIT(X)
 
D'ou X = X+2000 et non X+3000
 
Pourriez-vous m'aider à modifier mon code pour me permettre de souligner ce problème de concurrence ?
 
Merci d'avance !

n°571706
Roco
Posté le 20-11-2003 à 16:19:31  profilanswer
 

UPDATE DU CODE :
 
Classe principale :
 

Code :
  1. public class Main {
  2. public static void main(String[] args) {
  3.  Compte compteCh = new Compte(1000);
  4.  Compte compteCj = new Compte(1000);
  5.  Client clientCh = new Client("ch", compteCh);
  6.  Client clientCj = new Client("cj", compteCj);
  7.  Banque banqueBk = new Banque();
  8.  banqueBk.ajouterClient(clientCh);
  9.  Banque banqueBl = new Banque();
  10.  banqueBl.ajouterClient(clientCj);
  11.  Agent a1 = new Agent(banqueBk, banqueBl, clientCh, clientCj, 1, 500, 800, 800);
  12.  Agent a2 = new Agent(banqueBl, banqueBk, clientCj, clientCh, 2, 700, 500, 200);
  13.  new Thread(a1).start(); 
  14.         new Thread(a2).start(); 
  15.        
  16. }
  17. }


 
classe agent :
 

Code :
  1. public class Agent implements Runnable {
  2. int numero;
  3. double somme1;
  4. double somme2;
  5. double somme3;
  6. Banque b1;
  7. Banque b2;
  8. Client c1;
  9. Client c2;
  10. public Agent(Banque b1, Banque b2, Client c1, Client c2, int numero, double somme1, double somme2, double somme3) {
  11.  this.numero = numero;
  12.  this.somme1 = somme1;
  13.  this.somme2 = somme2;
  14.  this.somme3 = somme3;
  15.  this.b1 = b1;
  16.  this.b2 = b2;
  17.  this.c1 = c1;
  18.  this.c2 = c2;
  19. }
  20. public void passerTransaction(Client c1, Client c2, Banque b1, Banque b2, double somme) {
  21.  b1.debiterClient(c1, somme);
  22.  b2.crediterClient(c2, somme);
  23.  System.out.println("Transaction effectuee sur le client " + c1.getNom() + " par la processus " + numero);
  24. }
  25. public void run() {
  26.  passerTransaction(c1, c2, b1, b2, somme1);
  27.  try {
  28.   Thread.sleep((int)(Math.random()*1000));
  29.  }
  30.  catch (InterruptedException e) {}
  31.  passerTransaction(c1, c2, b1, b2, somme2);
  32.  try {
  33.   Thread.sleep((int)(Math.random()*1000));
  34.  }
  35.  catch (InterruptedException e) {}
  36.  passerTransaction(c1, c2, b1, b2, somme3);
  37.  try {
  38.   Thread.sleep((int)(Math.random()*1000));
  39.  }
  40.  catch (InterruptedException e) {}
  41.  System.out.println(c1.getCompte().getSolde());
  42.      System.out.println(c2.getCompte().getSolde());
  43.     }
  44. }


 
Je n'ai jamais d'incohérence alors que je ne gère pas de verrou sur le compte en banque. Peut-être que cela est dû au fait que passerTransaction soit effectué de manière atomique ?
 
Je rappelle que je veux justement récupérer une erreur pour prouver qu'il faut poser un verrou.

n°571710
BifaceMcLe​OD
The HighGlandeur
Posté le 20-11-2003 à 16:20:25  profilanswer
 

Pour le mettre en évidence, il faut que tu affiches la valeur du compte au milieu de la transaction. Mais il y a un problème dans ton code : tu te protèges déjà en partie contre les accès concurrents, en utilsiant la classe Vector.
 
Alors, règle importante : oublie la classe Vector en Java, c'est un reste de la première version du langage. Utilise des objets de type List à la place, et initialise-les par un "new ArrayList()". En termes d'implémentation, il n'y a pas de différence, sauf que Vector se protège contre les accès concurrents et ArrayList non.
 
Ensuite, si tu veux avoir quasiment à coup sûr une erreur dans tes transactions (une fois que tu es sûr de n'utiliser aucune synchronisation inter-thread nulle part, cf. paragraphe précédent), le mieux est sans doute de fixer un "point de rendez-vous" entre 2 threads au milieu de transaction, par un couple wait()/notify().

mood
Publicité
Posté le 20-11-2003 à 16:20:25  profilanswer
 

n°571712
Roco
Posté le 20-11-2003 à 16:21:39  profilanswer
 

super, je teste direct !

n°571717
Roco
Posté le 20-11-2003 à 16:22:19  profilanswer
 

en même temps, je vais virer vector et considérer kon a 1 seul client dans 1 seul banque, c con mais plus rapide !

n°571726
Roco
Posté le 20-11-2003 à 16:26:46  profilanswer
 

Tjrs po d'erreur !
 
Je désespère.
 
Qu'entends-tu par : "il faut que tu affiches la valeur du compte au milieu de la transaction " ?
 
Le résultat devrait de toute manière se voir à la fin.

n°571741
Rawhead re​x
Argghhhhh
Posté le 20-11-2003 à 16:38:30  profilanswer
 

Roco a écrit :

Tjrs po d'erreur !
 
Je désespère.
 
Qu'entends-tu par : "il faut que tu affiches la valeur du compte au milieu de la transaction " ?
 
Le résultat devrait de toute manière se voir à la fin.


Je pense qu'il voulais dire de pousser les threads à la faute en leur ordonnant d'effectuer leur operation en meme temps :
T1       T2
X+1000   Wait
Wait     Wait point de rendez-vous : quand T1 stop -> relance T2
Wair     repart : effectue X+2000
Wait     Wait
repart   Wait
Affiche X+2000

n°572282
BifaceMcLe​OD
The HighGlandeur
Posté le 21-11-2003 à 11:33:51  profilanswer
 

La transaction c'est d'abord un crédit, puis un débit, sur le même compte. On définit une règle de base qui dit qu'un compte ne peut pas avoir une balance négative, règle qu'on vérifie avant chaque transaction. Si tu ne te protèges pas contre les accès concurrents (concurrents dit forcément plusieurs comptes gérés en même temps) et que tu affiches la balance du compte après débit et avant crédit, tu peux te retrouver avec une balance négative malgré le test avant débit qui vérifiait que le débit pouvait se faire sans que le compte passe en négatif.
Tout simeplement parce qu'une autre transaction est venue manipuler le compte au beau milieu de la transaction en cours.

n°572675
Roco
Posté le 21-11-2003 à 15:42:23  profilanswer
 

Et donc... ?

n°572746
BifaceMcLe​OD
The HighGlandeur
Posté le 21-11-2003 à 17:22:06  profilanswer
 

Ben tu fais un println() entre les 2 !

Code :
  1. b1.debiterClient(c1, somme);
  2. double soldeIntermediaire = c1.getCompte().getSolde();
  3. if (soldeIntermediaire < 0.0) {
  4.   System.err.println("Solde débiteur en milieu de transaction pour le client " + c1.getNom() + ": " + solde);
  5. }
  6. b2.crediterClient(c2, somme);


Message édité par BifaceMcLeOD le 21-11-2003 à 17:22:22
n°572747
BifaceMcLe​OD
The HighGlandeur
Posté le 21-11-2003 à 17:24:18  profilanswer
 

BifaceMcLeOD a écrit :

(...) Si tu ne te protèges pas contre les accès concurrents (concurrents dit forcément plusieurs comptes gérés en même temps) (...)


Je rappelle qu'il te faut forcément plusieurs transactions sur les mêmes comptes en même temps pour mettre en évidence les erreurs dues à l'absence de synchronisation. Et plus il y en aura en même temps, plus tu as de chances de rencontrer une erreur.


Message édité par BifaceMcLeOD le 21-11-2003 à 17:25:09

Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  Java

  [NEW QUESTION] problème de concurrence et de verouillage en Java

 

Sujets relatifs
Question sur l'affichage d'une gif du Web...[JAVA]Ecrire lire et creer un fichier txt
ptite question sur les cadresHelp CPP type Java
Probleme de conversion[JAVA] Peut-on fixer le séparateur décimal à l'exécution ?
Question sur bouquin java...question sur les frames
Probleme de test sur un formulaire dynamique 
Plus de sujets relatifs à : [NEW QUESTION] problème de concurrence et de verouillage en Java


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR