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

Java Discussion :

Avis sur la bonne utilisation des Threads


Sujet :

Java

  1. #1
    Membre à l'essai
    Inscrit en
    Novembre 2005
    Messages
    19
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 19
    Points : 17
    Points
    17
    Par défaut Avis sur la bonne utilisation des Threads
    Bonjour,

    Je n'ai pas l'habitude de travailler avec les threads. c'est donc un domaine que je ne maitrise pas bien. J'aimerais donc bien, sans vouloir abuser, vous soumettre une archi et que vous me donniez votre avis. Merci.

    Le contexte est le suivante :
    Une servlet doit interroger un à x webservices que l'on appelle ici Enabler.

    Les contraintes sont les suivantes :
    Les webservices doivent être interrogés en parrallèle et non en séquentiel pour optimiser le temps de traitement de la servlet.
    Pour chaque enabler on renseigne un timeout qui correspond à un temps de traitement maximum. Si ce timeout est dépassé, la servlet continu son traitement sans attendre la retour du webservice.

    L'architecture que j'ai imaginé pour répondre à ce besoin est la suivante :
    - une classe AbstractEnabler
    Cette classe abstraite hérite de Thread. La méthode run() est déclarée en final et des méthodes abstarites get() et set() sont déclarées (les méthodes set() et get() correspondent aux 2 types d'actions qu'il est possible de faire dans chaque enabler).
    Cette classe AbstractEnabler sert de base à la conception des enablers qui vont être appellés dans mes servlets.

    - une classe EnablerRunner
    Cette classe hérite également de Thread. Comme son nom l'indique cette classe va servir a exécuter les différents Enabler. Le Runner va également s'assurer que le traitement de l'Enabler ne dépasse pas la durée du timeout.

    On a donc 1 servlet qui va lancer 1 à x EnablerRunner et chaque EnablerRunner va lancer une classe héritant d'AbstractEnabler.

    Mes premiers tests en local semblent concluant et j'ai quelque chose qui répond à ce qui a été demandé. Maintenant, comme je vous l'ai dit, je ne maitrise pas bien les threads et je me demande si il n'y a pas moyen de faire cela en une seule classe?

    Si ca vous interresse, voici le code.

    la classe AbstractEnabler :
    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
     
    public abstract class AbstractEnabler extends Thread /*implements Runnable*/ {
     
    	private String server = "127.0.0.1";
    	private int port = 8080;
    	private int timeout = 0;
    	private int activation = 1;
     
    	protected Logger log = FileLogger.getLogger(this.getClass());
     
     
    	/**
             * Constructeur vide
             *
             */
    	public AbstractEnabler() {
    	}
     
    	/**
             * Initialise l'enabler avec ses différentes propriétés
             * 
             * @param name
             *      le nom
             * @param server
             *      le server (adresse IP ou nom réseau)
             * @param port
             *      le port
             * @param timeout
             *      le timeout
             * @param activation
             *      1 l'enabler est actif, 0 il est inactif
             */
    	public AbstractEnabler(String name, String server, int port, int timeout, int activation) {
    		super();
    		setName(name);
    		setServer(server);
    		setPort(port);
    		setTimeout(timeout);
    		setActivation(activation);
     
    		debug();
    	}
     
    	/**
             * Initialise l'enabler avec EnablerDefinition
             * 
             * @param definition
             *      l'objet EnablerDefinition
             */
    	public AbstractEnabler(EnablerDefinition definition) {
    		setName(definition.getName());
    		setServer(definition.getServer());
    		setPort(definition.getPort());
    		setTimeout(definition.getTimeout());
    		setActivation(definition.getActivation());
     
    		debug();
    	}
     
    	/**
             * ici se trouvent normalement les getters et les setters
             */
     
    	private void debug() {
    		log.debug("le nom de cet enabler est : " + this.getName());
    		log.debug("le serveur de cet enabler est : " + this.getServer());
    		log.debug("le port de cet enabler est : " + this.getPort());
    		log.debug("le timeout de cet enabler est : " + this.getTimeout());
    		log.debug("le flag activation de cet enabler est : " + this.getActivation());
    	}
     
    	/**
             * @see java.lang.Runnable#run()
             */
    	public final void run() {
    		// je force le get() pour les tests
    		this.get();
    	}
     
    	public abstract void set();
     
    	public abstract void get();
    la classe EnablerRunner :
    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
     
    public class EnablerRunner extends Thread {
     
    	private AbstractEnabler enablerThread;
     
    	private Logger log = FileLogger.getLogger(this.getClass());
     
    	public EnablerRunner(AbstractEnabler enabler) {
    		enablerThread = enabler;
    	}
     
    	/**
             * @see java.lang.Runnable#run()
             */
    	public void run() {
    		try {
    			log.debug("Démarrage du Thread " + enablerThread.getName() + ".");
    			enablerThread.start();
    			log.debug("Le Thread " + enablerThread.getName() + " est démarré.");
     
    			if (enablerThread.getTimeout() > 0) {
    				log.debug("Le Thread " + enablerThread.getName() + " a un timeout de " + enablerThread.getTimeout() + " millisecondes.");
     
    				// on attend la fin du thread enabler pour une durée au maximum égale
    				// au timeout (en millisecondes)
    				enablerThread.join(enablerThread.getTimeout());
     
    				if (enablerThread.isAlive()) {
    					log.debug("Interruption du Thread " + enablerThread.getName() + ".");
    					enablerThread.interrupt();
    					enablerThread = null;
    				}
    			}
     
    			if (enablerThread != null) {
    				log.debug("Le Thread " + enablerThread.getName() + " est terminé.");
    			}
     
    		} catch (InterruptedException e) {
    			log.debug("Thread " + enablerThread.getName() + " interrompu.");
    			e.printStackTrace();
    		}
    	}
    }
    et voici cmment je teste :
    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
     
    public class TestDefaultEnabler {
     
    	/**
             * @param args
             */
    	public static void main(String[] args) {
    		EnablerDefinition definition = Configuration.getInstance().getEnablerDefinition("URM");
     
    		AbstractEnabler enabler = new DefaultEnabler(definition);
    		EnablerRunner runner = new EnablerRunner(enabler);
     
    		runner.start();
    	}
     
    }

    merci à ceux qui prendront le temps de me lire.

  2. #2
    Membre expérimenté
    Avatar de Patriarch24
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Septembre 2003
    Messages
    1 047
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2003
    Messages : 1 047
    Points : 1 640
    Points
    1 640
    Par défaut
    Dans ton cas, tu ne devrait pas hériter de Thread, mais de Runnable. En effet, ta classe Enabler n'est pas un Thread, mais s'exécute dans un Thread, ce qui est différent.
    Jette un oeil sur les API de synchronisation (java.util.concurrent) si tu utilises J2SE 5. Ton EnablerRunner n'est rien d'autre qu'un Executor, qui exécute donc un Runnable (ton Enabler).
    http://java.sun.com/j2se/1.5.0/docs/...e-summary.html
    En premier lieu, utilisez un moteur de recherche.
    En second lieu, postez sur le forum adéquat !

  3. #3
    Membre à l'essai
    Inscrit en
    Novembre 2005
    Messages
    19
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 19
    Points : 17
    Points
    17
    Par défaut
    Merci,

    Je ne connaissais pas cette nouveauté de Java 5. Effectivement, mon Runner n'est ni plus ni moins qu'un Executor. N'ayant pas l'ambition de réinventer la roue, je vais utiliser l'executor. J'ai vu aussi que Java 5 met à dispostion l'interface Callable qui est bien plus interressante que Runable puisqu'elle permet de retourner un objet.

    Je vais tester ca de ce pas.

  4. #4
    Membre à l'essai
    Inscrit en
    Novembre 2005
    Messages
    19
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 19
    Points : 17
    Points
    17
    Par défaut
    L'API de concurrence semble très bien mais elle à un gros défaut majeur. Elle ne permet pas d'executer plusieurs Runnable en même temps. Je n'arrive qu'à les executer de manière séquentielle.

  5. #5
    Membre à l'essai
    Inscrit en
    Novembre 2005
    Messages
    19
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 19
    Points : 17
    Points
    17
    Par défaut
    Bon,

    En revenant à une solution proche dans le principe de ce que j'ai mis au début, j'arrive à lancer plusieurs threads en même temps. Mais, car il y a bien sur un mais, le timeout de chaque thread est à peu près multiplié par le nombre de threads qui est lancé.

    Donc si un thread a un timeout de 500 millisecondes, si j'en lance 4 en même temps, il vont chacun tourner simultanément pendant à peu près deux secondes. J'ai essayé avec plusieurs valeurs de timeout et je retombe toujous à peut près sur ca.

    Ma question... Est ce normal? J'avais bien dit être débutant en thread. Je concois parfaitement qu'en faisant tourner plusieurs threads simultanment la durée d'execution de chaque thread soit supérieure à ce qu'elle aurait été si le thread tournais seul. Mais un terl rapport, je trouve ca quand même assez énorme.

  6. #6
    Membre à l'essai
    Inscrit en
    Novembre 2005
    Messages
    19
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 19
    Points : 17
    Points
    17
    Par défaut
    Suite et fin.... ouf

    J'ai trouvé la solution Pour lancer x threads en simultanés avec un timeout. Pour infor, le problème que je décrivais tout à l'heure (temps d'execution des thread) était du à une utilisation hasardeuse de la méthode join() de la classe thread.

    Donc pour ceux que ca interresse, la solution que j'ai mis en place est la suivante :

    Une classe GenericEnabler :
    Cette classe ne fait pas grand chose. Elle sert juste à initialiser les traitements avec les bon paramètres et à les lancer.
    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
     
    public class GenericEnabler {
     
    	private String name = "Default";
    	private String server = "127.0.0.1";
    	private int port = 8080;
    	private int timeout = 0;
    	private int activation = 1;
     
    	protected Logger log = FileLogger.getLogger(this.getClass());
     
     
    	/**
             * Constructeur vide
             *
             */
    	public GenericEnabler() {
    	}
     
    	/**
             * Initialise l'enabler avec ses différentes propriétés
             * 
             * @param name
             *      le nom
             * @param server
             *      le server (adresse IP ou nom réseau)
             * @param port
             *      le port
             * @param timeout
             *      le timeout
             * @param activation
             *      1 l'enabler est actif, 0 il est inactif
             */
    	public GenericEnabler(String name, String server, int port, int timeout, int activation) {
    		super();
    		setName(name);
    		setServer(server);
    		setPort(port);
    		setTimeout(timeout);
    		setActivation(activation);
     
    		debug();
    	}
     
    	/**
             * Initialise l'enabler avec EnablerDefinition
             * 
             * @param definition
             *      l'objet EnablerDefinition
             */
    	public GenericEnabler(EnablerDefinition definition) {
    		setName(definition.getName());
    		setServer(definition.getServer());
    		setPort(definition.getPort());
    		setTimeout(definition.getTimeout());
    		setActivation(definition.getActivation());
     
    		debug();
    	}
     
     
    	/**
             * les getters et les setters [...]
             */
     
     
    	private void debug() {
    		log.debug("le nom de cet enabler est : " + this.getName());
    		log.debug("le serveur de cet enabler est : " + this.getServer());
    		log.debug("le port de cet enabler est : " + this.getPort());
    		log.debug("le timeout de cet enabler est : " + this.getTimeout());
    		log.debug("le flag activation de cet enabler est : " + this.getActivation());
    	}
     
    	public final void execute(AbstractCommand command) {
    		CommandExecutor executor = new CommandExecutor(command, this.getTimeout());
     
    		if (executor != null) {
    			executor = null;
    		}
    	}
     
    	public final void execute(List<? extends AbstractCommand> commands) {
    		CommandExecutor executor = new CommandExecutor(commands, this.getTimeout());
     
    		if (executor != null) {
    			executor = null;
    		}
    	}
    Une Classe AbstractCommand :
    C'est une classe abstraite qui implémente Runnable. La méthode run de l'interface Runnable est définie en abstraite.
    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
     
    public abstract class AbstractCommand implements Runnable {
     
    	private String server = "127.0.0.1";
    	private int port = 8080;
    	private final String name;
     
    	public AbstractCommand(String server, int port) {
    		setServer(server);
    		setPort(port);
    		name = this.getClass().getSimpleName();
    	}
     
    	/**
             * les getters et les setters [...]
             */
     
    	public abstract void run();
    }
    Voici un exemple d''utilisatation de ctte classe abstraite.
    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
     
    public class DefaultCommand extends AbstractCommand {
     
    	private Logger log = FileLogger.getLogger(this.getClass());
     
    	public DefaultCommand(String server, int port) {
    		super(server, port);
    	}
     
    	@Override
    	public void run() {
    		int timer = 0;
     
    		try {
    			while (timer < 4000) {
    				timer += 100;
    				Thread.sleep(100);
    				log.debug("DefaultCommand " + this.getServer() + " actif depuis " + timer + " millisecondes.");
    			}
    		} catch (InterruptedException ie) {
    			log.debug("DefaultCommand " + this.getServer() + " arrété au bout de " + timer + " millisecondes.");
    		}	
    	}
     
    }
    Une classe CommandExcecutor :
    C'est cette classe qui va exécuter dans des threads différents toutes les commandes qu'elle aura dans une pile.
    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
     
    public class CommandExecutor {
     
    	private List<Thread> threads = new ArrayList<Thread>();
    	private int timeout;
     
    	private Logger log = FileLogger.getLogger(this.getClass());
     
    	public CommandExecutor(AbstractCommand command, int timeout) {
    		Thread thread = new Thread(command);
    		thread.setName(command.getName());
    		threads.add(thread);
    		this.timeout = timeout;
     
    		this.start();
    	}
     
    	public CommandExecutor(List<? extends AbstractCommand> commands, int timeout) {
    		for (AbstractCommand command : commands) {
    			Thread thread = new Thread(command);
    			thread.setName(command.getName());
    			threads.add(thread);
    		}
    		this.timeout = timeout;
     
    		this.start();
    	}
     
    	public synchronized void start() {		
    		try {
    			int timer = 0;
     
    			for (Thread thread : threads) {
    				log.debug("Démmarage de la commande " + thread.getName() + ".");
    				thread.start();
    				log.debug("La commande " + thread.getName() + " est démarrée.");
    			}
     
    			if (timeout > 0) {
    				while (timer < timeout) {
    					Thread.sleep(100);
    					timer += 100;
    				}
     
    				for (Thread thread : threads) {
    					if (thread.isAlive()) {
    						log.debug("Interruption de la commande " + thread.getName() + " au bout de " + timer + " millisecondes.");
    						thread.interrupt();
    					}
    				}
    			}
     
    			for (Thread thread : threads) {
    				log.debug("La commande " + thread.getName() + " est terminée.");
    			}
     
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    C'est dans cette dernière classe que ce trouvait donc le problème avec la méthode join. Je l'ai donc réslu en n'employant plus la méthode join de l'objet Thread. la place, je lance tous mes Threads à la suite et après je fais cette boucle.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while (timer < timeout) {
    	Thread.sleep(100);
    	timer += 100;
    }
    Bref, j'ai fais plein de tests et ca marche en local. Maintenant faut voir ce que ca donne dans une servlet avec des appels à des webservices... mais ca s'est une autre histoire.

    Pour ma culture personelle. Si il est possible de faire la même chose avec un Executor, ca m'interrese. J'ai cherché, peut être mal, et je n'ai pas trouvé.

  7. #7
    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
    Points : 3 080
    Points
    3 080
    Par défaut
    Pfiou... Tu te compliques joliment. Avec en plus de l'attente active, et un sleep(...) pour attendre un délai : c'est pas très joli, et c'est plus cher en ressources.

    Je suis désolé, j'ai pas tout compris ce que tu voulais faire, mais d'après moi c'est faisable avec les Executors, en mettant les bonnes actions à effectuer dans tes Runnable...

  8. #8
    Membre à l'essai
    Inscrit en
    Novembre 2005
    Messages
    19
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 19
    Points : 17
    Points
    17
    Par défaut
    Citation Envoyé par ®om
    Pfiou... Tu te compliques joliment. Avec en plus de l'attente active, et un sleep(...) pour attendre un délai : c'est pas très joli, et c'est plus cher en ressources.
    C'est possible. les thread c'est pas mon truc donc....

    Citation Envoyé par ®om
    Je suis désolé, j'ai pas tout compris ce que tu voulais faire, mais d'après moi c'est faisable avec les Executors, en mettant les bonnes actions à effectuer dans tes Runnable...
    executer x threads en simultanés et pouvoir leur imposer une durée maximum d'execution. C'est le simultanés que je n'ai pas réussi à faire avec un Executor. J'ai par exemple essayé avec un ThreadPoolExecutor et quelque soit la facon dont je m'y prenais ca executait les threads en sequentiel.

  9. #9
    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
    Points : 3 080
    Points
    3 080
    Par défaut
    Citation Envoyé par Pitivier
    executer x threads en simultanés et pouvoir leur imposer une durée maximum d'execution. C'est le simultanés que je n'ai pas réussi à faire avec un Executor. J'ai par exemple essayé avec un ThreadPoolExecutor et quelque soit la facon dont je m'y prenais ca executait les threads en sequentiel.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Executors.newFixedThreadPool(int nThreads);

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

Discussions similaires

  1. Des avis sur une bonne méthode de développement
    Par Neilime05 dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 1
    Dernier message: 29/07/2010, 14h00
  2. Réponses: 1
    Dernier message: 10/04/2010, 09h10
  3. utilisation des threads sur C++ builder 6
    Par badardar dans le forum C++
    Réponses: 3
    Dernier message: 16/04/2008, 12h17
  4. [C#] La bonne utilisation des WinForms (ouverture-Fermeture)
    Par Harry dans le forum Windows Forms
    Réponses: 28
    Dernier message: 03/08/2005, 11h39

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