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

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

Marsh Posté le 20-11-2003 à 15:00:03    

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
Reply

Marsh Posté le 20-11-2003 à 15:00:03   

Reply

Marsh Posté le 20-11-2003 à 15:04:20    

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
Reply

Marsh Posté le 20-11-2003 à 15:04:55    

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
Reply

Marsh Posté le 20-11-2003 à 15:06:04    

lorill a écrit :

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


t'aurais du me quoter  :kaola:


---------------
ma vie, mon oeuvre - HomePlayer
Reply

Marsh Posté le 20-11-2003 à 15:07:19    

benou a écrit :


t'aurais du me quoter  :kaola:  

[:absynthe]

Reply

Marsh Posté le 20-11-2003 à 15:11:43    

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

Reply

Marsh Posté le 20-11-2003 à 15:19:08    

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 ;)

Reply

Marsh Posté le 20-11-2003 à 16:03:33    

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 !

Reply

Marsh Posté le 20-11-2003 à 16:19:31    

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.

Reply

Marsh Posté le 20-11-2003 à 16:20:25    

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().

Reply

Marsh Posté le 20-11-2003 à 16:20:25   

Reply

Marsh Posté le 20-11-2003 à 16:21:39    

super, je teste direct !

Reply

Marsh Posté le 20-11-2003 à 16:22:19    

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 !

Reply

Marsh Posté le 20-11-2003 à 16:26:46    

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.

Reply

Marsh Posté le 20-11-2003 à 16:38:30    

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

Reply

Marsh Posté le 21-11-2003 à 11:33:51    

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.

Reply

Marsh Posté le 21-11-2003 à 15:42:23    

Et donc... ?

Reply

Marsh Posté le 21-11-2003 à 17:22:06    

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
Reply

Marsh Posté le 21-11-2003 à 17:24:18    

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
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed