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 :

MultiThread comment faire ?


Sujet :

Langage Java

  1. #1
    Membre confirmé
    Inscrit en
    Juin 2005
    Messages
    139
    Détails du profil
    Informations forums :
    Inscription : Juin 2005
    Messages : 139
    Par défaut MultiThread comment faire ?
    Bonjour à toute et à tous,
    premièrement ne vous focaliser pas sur l'exemple car je l'ai calqué un peu par rapport aux objets que je manipule sur le projet de ma société.
    ce que je voudrai faire c'est d’exécuter plusieurs Thread Java afin d'aller plus vite au niveau du traitement. bien évidement mon projet de société est plus gros et du coup il prend beaucoup de temps.
    La methode que je veux multithreadé est fetchMap()
    Je voudrai lancer 10 Threads en parallèle sur cette méthode.

    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
     
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
     
    public class MultiThread  {
     
     
    	Map<Personne, List<Animal>> mapP = new HashMap<>();
    	List<Animal> list = new ArrayList<>();
     
    	public MultiThread(){
    		init();
    		fetchMap();
    	}
     
    	public void init(){
    		Personne p0= new Personne("Dupont","Eric",00);
    		Personne p1= new Personne("Durant","Etienne",10);
    		Personne p2= new Personne("Newton","Isaac",20);
    		Personne p3= new Personne("Sartre","Paul",30);
     
    		Animal a0= new Animal("Chien");
    		Animal a1= new Animal("Chat");
    		Animal a2= new Animal("Lion");
    		Animal a3= new Animal("Tigre");
     
    		list.add(a0);
    		list.add(a1);
    		list.add(a2);
    		list.add(a3);
     
    		mapP.put(p0, list);
    		mapP.put(p1, list);
    		mapP.put(p2, list);
    		mapP.put(p3, list);
    	}
     
    	public void fetchMap(){
    		Set<Personne> listKeys=mapP.keySet(); 
    		Iterator<Personne> iterateur=listKeys.iterator();
     
    		while(iterateur.hasNext())
    		{
    			Personne personne= iterateur.next();
    			StringBuffer str = new StringBuffer();
     
    			for (Animal animal : mapP.get(personne)) {
     
    					str.append(animal.getType() + " ");
    			}
    			System.out.println ("M. " + personne.getNom() + " Possede les animaux suivant : " + str);
    		}
    	}
     
    	public static void main(String[] args) {
     
    		MultiThread main =new MultiThread(); 
    	}
     
     
    }
    Le code de la classe Personne et Animal est facilement deductible. juste qq attributs et rien de particuliers.
    Je vous serai reconnaissant.

    Cordialement

  2. #2
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    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 483
    Par défaut
    Juste quelques remarques:

    Ton code ne fait rien d'utile donc difficile de savoir ce que tu veux paralléliser ou de savoir si l'ordre de traitement est important.
    On ne parcoure jamais une map par KeySet pour ensuite faire un get(), on parcours directement par entrySet, c'est bien plus performant. Un get sur une map prend LOG(N), tu as donc un algo en NLog(N) alors qu'un parcours par entrySet fait du N.
    Qu'est-ce qui est lent dans ton problème?
    Qu'est-ce qui te coince dans ta parallélisation?
    Qu'est-ce qui te fait croire que la parallélisation est une solution à ton problème?


    Si tu veux parcourir une liste d'animaux pour faire quelque chose sur chaque animal, oui tu peux paralléliser ça. Via un parallelStream par exemple, avec java 8.
    Pareil pour le parcours d'une map.

  3. #3
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    Accessoirement, tes 10 threads parcoureraient les mêmes collections/maps d'éléments, et afficheraient tous la même chose, à moins de synchroniser les accès en lecture aux éléments de ta map ce qui ne ferait que ralentir encore plus l'application (au point qu'elle soit plus lente qu'une appli sans threads)

  4. #4
    Membre confirmé
    Inscrit en
    Juin 2005
    Messages
    139
    Détails du profil
    Informations forums :
    Inscription : Juin 2005
    Messages : 139
    Par défaut
    Merci Tchiz et eulbobo pr vos remarques.
    Comme j'ai évoqué mon code est adapté ici par rapport au code de la société pr ne pas qu'on le reconnaisse sur le site.
    Le but est de remplir une liste d'objet ( là j'ai juste mis les afichages) mais en vérité ça remplit une liste ou par exemple on mettrai toutes les personnes et la liste des animaux (c'est encore une fois un exemple).
    donc je voulais savoir si je lance 10 Threads sur la même méthode ( fetchMap), il y'a un risque que ca m'affiche 10fois par chaque thread alors que moi je veux un traitement pour chaque élément de la liste.
    Pour illustrer : le premier thread par exemple traite le premier element de la liste, le 2eme thread traite le 2eme élément de la liste(et en aucun cas le 2eme thread traite le premier élément car il est déjà traité par le 1eme thread.)
    si j'insiste c'est que j'ai un batch java qui prend environ 3h pour traiter toutes les informations.
    Merci tchize_ pour la subtilité sur le parcours des maps.



    cordialement
    Images attachées Images attachées  

  5. #5
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    Du coup, comme je le disais, il va falloir faire en sorte de synchroniser tout ça

    Synchroniser l'accès en lecture à l'élément next de ton itérateur sur l'entrySet (deux threads ne doivent pas travailler sur le même élément)
    Synchroniser l'accès en écriture sur ta liste finale (là ça va être très coûteux)


    Autre possibilité : chaque Thread possède sa propre liste de résultats à ajouter, et à la fin du traitement, tu fais un merge de tous les résultats de chaque Thread. (avec les interfaces Callable et Future)


    Il te faudrait donc
    Une méthode synchronisée d'une manière ou d'une autre qui permette à tes threads d'accéder les uns après les autres à ton itérateur de map (je conseille l'utilisation d'un ReentrantReadWriteLock en utilisant en lock de type readerLock).
    Un pool de thread (Executors.newFixedThreadPool(10) )
    Une classe qui implémente Callable à laquelle tu passes la valeur de ton EntrySet et qui te renvoie ce dont du as besoin (que tu peux ajouter à ta liste)

    Par contre, attention ! La programmation multithread est quelque chose de délicat auquel il faut faire très attention : on peut avoir l'impression de gagner du temps mais au final on peut se retrouver dans des situations dans lesquelles on ralenti les choses plus qu'autre chose à cause d'un syndrome de bottleneck

    D'ailleurs, pourquoi 10 threads? (la réponse est importante)

  6. #6
    Membre confirmé
    Inscrit en
    Juin 2005
    Messages
    139
    Détails du profil
    Informations forums :
    Inscription : Juin 2005
    Messages : 139
    Par défaut
    Encore merci eulbobo

    Pourquoi 10 Threads? je sais pas. je me suis dis que 10 devraient suffir.
    tu as mieux comme idée?
    apres tous ce que tu m'as dis je maitrise rien.
    Je n'ai pas le niveau expert comme toi :-(
    si tu as un exemple simple je veux bien.
    Merci infiniment.

  7. #7
    Membre Expert
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Par défaut
    La question n'est pas d'être expert ou débutant, mais de bien connaitre les dangers !

    Pourquoi je te demande d'où tu sors ce chiffre de 10 threads? Parce que c'est une question piège. Chaque thread lancé utilisé un processeur. Du coup, sur une machine à 4 processeurs, tu auras au mieux 4 threads qui tourneront en même temps. Sachant que ton application principale en utilise déjà un, tu n'aurais en théorie que 3 de tes threads spécifiques qui pourraient travailler et les 7 autres qui attendraient d'avoir du temps de disponible !

    Du coup, pour déterminer le nombre de processeurs à utiliser, mieux vaut viser volontairement bas (2 ou 3), ou se fixer sur le nombre max de processeurs disponibles sur la machine (sachant que ce n'est pas parce que la machine a 10 processeurs de disponibles qu'elle vous laissera en utiliser plus de deux)
    Autre point : quand un programme prend 1 heures, le faire tourner sur deux thread ne réduit pas le temps d'exécution par deux (ça serait trop simple), et le faire tourner sur 4 ne permet pas d'obtenir un résultat en 1/4 d'heure. Ca va plus vite, mais tu atteins rapidement un point à partir duquel tes performances se réduisent (du fait de la sollicitation des ressources de la machine)

    Autre élément à savoir, ce qui ralenti le plus le travail des threads entre eux, c'est l'accès à des ressources partagées, comme de la lecture/écriture sur une liste commune. Les méthodes "synchronized" sont aussi une horreur en terme de vitesse d'exécution (le mot clé force une vérification puis un lock, ça prend du temps)


    Du coup, l'idée c'est de développer en utilisant un système qui soit permette d'éviter le plus possible des blocages, tout en conservant une cohérence générale.

    Avec un code comme suit, on itère dans une méthode générale, et pour chaque élément, on utilise un thread différent
    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
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
     
    public class MultiThread {
     
    	Map<Personne, List<Animal>> mapP = new HashMap<>();
    	List<Animal> list = new ArrayList<>();
     
    	public void init() {
    		Personne p0 = new Personne("Dupont", "Eric", 00);
    		Personne p1 = new Personne("Durant", "Etienne", 10);
    		Personne p2 = new Personne("Newton", "Isaac", 20);
    		Personne p3 = new Personne("Sartre", "Paul", 30);
     
    		Animal a0 = new Animal("Chien");
    		Animal a1 = new Animal("Chat");
    		Animal a2 = new Animal("Lion");
    		Animal a3 = new Animal("Tigre");
     
    		list.add(a0);
    		list.add(a1);
    		list.add(a2);
    		list.add(a3);
     
    		mapP.put(p0, list);
    		mapP.put(p1, list);
    		mapP.put(p2, list);
    		mapP.put(p3, list);
    	}
     
    	// notre exécuteur, qui renvoie un résultat de type String
    	public class MyWorker implements Callable<String> {
     
    		private Entry<Personne, List<Animal>> key;
     
    		// je passe l'Entry par fainéantise, normalement on devrait passer les deux éléments séparemment
    		public MyWorker(Entry<Personne, List<Animal>> key) {
    			this.key = key;
    		}
     
    		@Override
    		public String call() throws Exception {
    			StringBuilder sb = new StringBuilder();
    			sb.append(Thread.currentThread().getName() + " M. " + key.getKey().getNom() + " Possede les animaux suivant : ");
    			for (Animal animal : key.getValue()) {
    				sb.append(animal.getType() + " ");
    			}
    			return sb.toString();
    		}
     
    	}
     
    	// throws Exception mais normalement on devrait gérer mieux les exceptions, ici aussi, fainéantise de ma part, mais c'est pour l'exemple
    	public static void main(String[] args) throws Exception {
    		MultiThread mt = new MultiThread();
    		mt.init();
    		// création d'un pool de threads en fonction du nombre de processeurs
    		ExecutorService ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
     
    		// notre liste de "futurs" résultats
    		List<Future<String>> results = new ArrayList<>();
    		for (Entry<Personne, List<Animal>> key : mt.mapP.entrySet()) {
    			// lancement d'un thread et ajout du résultat à la liste finale
    			results.add(ex.submit(mt.new MyWorker(key)));
    			// chaque submit lance le thread
    		}
    		// lecture des résultats, chaque get récupère un résultat (et attend un résultat si besoin)
    		for (Future<String> value : results) {
    			System.out.println(value.get());
    		}
    		// fermeture du pool (obligatoire)
    		ex.shutdown();
    	}
     
    }
    Dans l'exemple, j'ai fait en sorte qu'aucun des threads ne travaille sur le même élément, et aucun n'essaye d'accéder à des éléments "communs" en même temps, en lecture ou en écriture.
    Et l'ordre de lecture des éléments est piloté par le thread principal qui indique aux threads l'élément à traiter

    J'espère que ça pourra t'aider


    PS : petit bonus
    En ajoutant des pistes de logs et des pauses, on peut voir comment les éléments fonctionnent
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    		public String call() throws Exception {
    			System.out.println(Thread.currentThread().getName() + "Start");
    			StringBuilder sb = new StringBuilder();
    			sb.append(Thread.currentThread().getName() + " M. " + key.getKey().getNom() + " Possede les animaux suivant : ");
    			for (Animal animal : key.getValue()) {
    				Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
    				sb.append(animal.getType() + " ");
    			}
    			System.out.println(Thread.currentThread().getName() + "finished");
    			return sb.toString();
    		}
    Donne comme résultat

    pool-1-thread-1Start
    pool-1-thread-3Start
    pool-1-thread-2Start
    pool-1-thread-4Start
    pool-1-thread-3finished
    pool-1-thread-4finished
    pool-1-thread-2finished
    pool-1-thread-1finished
    pool-1-thread-1 M. Dupont Possede les animaux suivant : Chien Chat Lion Tigre
    pool-1-thread-2 M. Newton Possede les animaux suivant : Chien Chat Lion Tigre
    pool-1-thread-3 M. Durant Possede les animaux suivant : Chien Chat Lion Tigre
    pool-1-thread-4 M. Sartre Possede les animaux suivant : Chien Chat Lion Tigre
    Ou lorsqu'on relance
    pool-1-thread-1Start
    pool-1-thread-3Start
    pool-1-thread-2Start
    pool-1-thread-4Start
    pool-1-thread-3finished
    pool-1-thread-2finished
    pool-1-thread-1finished
    pool-1-thread-1 M. Durant Possede les animaux suivant : Chien Chat Lion Tigre
    pool-1-thread-2 M. Dupont Possede les animaux suivant : Chien Chat Lion Tigre
    pool-1-thread-3 M. Sartre Possede les animaux suivant : Chien Chat Lion Tigre
    pool-1-thread-4finished
    pool-1-thread-4 M. Newton Possede les animaux suivant : Chien Chat Lion Tigre

  8. #8
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    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 483
    Par défaut
    Quelques remarques: ton batch prends 3 heures et tu veux améliorer. As-tu commencé par un profiling de ton applicaiton? Le passage en multithread, c'est ce que l'on fait quand on a épuisé toutes les autres options. Il faut d'abord faire un profiling pour trouver les points chauds de ton application et les améliorer. Je t'ai déjà pointé un problème dans ton parcours de map dans ton exemple. Quelque chose me dit que ton code doit être plein d'autres tests inutiles dans le même genre.

    Ensuite, Si tu as un traitement X que tu applique à N éléments d'une liste et que tu veux appliquer ça en parallèle, comme je l'ai dit un parallelStream fait le travail. Sinon, il y a l'option de mettre chaque tâche à faire dans un ThreadPoolExecutor que tu calibre en fonction des CPUs disponibles.

    Aussi, si c'est un batch qui prend 3h sur un CPU, pas sur que les admins du serveur apprécient que tu transforme ça en 40 minutes sur l'ensemble des CPUs. Dans le premier cas la machine reste disponible, dans le second le serveur est mort pendant 40 minutes

  9. #9
    Membre confirmé
    Inscrit en
    Juin 2005
    Messages
    139
    Détails du profil
    Informations forums :
    Inscription : Juin 2005
    Messages : 139
    Par défaut
    Merci bcp mes amis

    Alors pour le parcours de la map j'ai corrigé.
    ensuite j'ai refais ma requête sql pr Oracle afin de réduire au max le nombre d’accès en base car ma méthode fetchMap fais au moins 3 accès bases par itération. aujourd’hui il en fais aucun
    pour le parallelStream je ne sais pas comment ça fonctionne mais je vais creuser la piste.
    Je suis en JDK7 sur le serveur de prod donc la version 8 c'est pas pour toute suite.
    Merci eulbobo pour ton code. je vais tester ça.
    et merci pour l'explication sur les threads.
    Je pensais que c'etais simple mais finalement c'est pas aussi facile que ça.
    Le traitement fait mois de 2h today
    le seul hic c'est pour les testes et je suis obligé de faire le teste sur toutes les données.
    je pense que je peux toujours ameliorer
    grace à vous biensur.
    Celui qui n'est pas reconnaissant est un ingrat
    je vous suis reconaissant

    Merci bcp.

Discussions similaires

  1. [VB6][impression]Comment faire des effets sur les polices ?
    Par le.dod dans le forum VB 6 et antérieur
    Réponses: 11
    Dernier message: 08/11/2002, 11h31
  2. comment faire evoluer ma base vers interbase6
    Par toure32 dans le forum InterBase
    Réponses: 5
    Dernier message: 23/10/2002, 11h59
  3. Réponses: 8
    Dernier message: 18/09/2002, 04h20
  4. Comment faire pour mettre l'ecran en veille ?
    Par March' dans le forum MFC
    Réponses: 6
    Dernier message: 29/08/2002, 15h25
  5. Comment faire pour créer un bitmap
    Par GliGli dans le forum C++Builder
    Réponses: 2
    Dernier message: 24/04/2002, 16h41

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