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 :

Problème Thread et variable static


Sujet :

Concurrence et multi-thread Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    45
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 45
    Par défaut Problème Thread et variable static
    Voici mon code :

    Je lis dans un buffer les données ligne par ligne et je les mets dans un variable result qui est en static.
    Ensuite, j'affiche le result lorsque je sors de mon Thread. Pourquoi est -il vide?

    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
     
    private static String result = "";
    public static void getTextOutput(final Process p) {
    		new Thread() {
    			public void run() {
    				try {
    					BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
    					String lineBuffered = "";
    					try {
    						result = reader.readLine();
    						while((lineBuffered = reader.readLine()) != null) {
    							result += "\n" + lineBuffered;
    						}
    					} finally {
    						reader.close();
    					}
    				} catch(IOException ioe) {
    					ioe.printStackTrace();
    				}
    			}
    		}.start();
    		monLog.log(Level.WARNING," TEXT " +  result );
    }

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    La ligne monLog.log(Level.WARNING," TEXT " + result ); s'exécute bien avant que le tread ne commence à lire des données.

    Tu dois bien évidemment attendre que le thread se termine, avant d'essayer de lire ses résultats -_-°. ... Et du coup on peut se demander à quoi ça servait de créer un thread puisque tu attends juste qu'il se termine.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre averti
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    45
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 45
    Par défaut
    Ok !

    J'exécute des lignes de commandes avec Runtime.getRuntime.exec()

    Pour communiquer avec le processus executé, les différents flux doivent être traités depuis des threads différents, pour éviter des inter blocage!
    http://ydisanto.developpez.com/tutor.../runtime-exec/

  4. #4
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    Ok, mais dans l'exemple que tu donnes, il n'y a pas d'interaction (tu lis la sortie de l'autre processus, mais tu ne lui envoies rien du tout en entrée.) Il n'y a donc pas de risque d'interblocage.
    ... Mais dans des cas plus interactifs, effectivement.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre très actif
    Avatar de la.lune
    Homme Profil pro
    Directeur Technique
    Inscrit en
    Décembre 2010
    Messages
    548
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Comores

    Informations professionnelles :
    Activité : Directeur Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2010
    Messages : 548
    Par défaut
    Pour éviter des interblocages si tu as une source partagé alors il faut penser à la synchronisation de la ressource.

    Bon juste pour ton cas là affiché dans l'exemple si tu veux avoir le résultat avec les données.

    N'importe où tu te trouves si tu es obligé de faire cette succession c'est simple, met ton/tes Thread(s) dans une/des variable(s) et fais appel la méthode thread.join() sur ton/tes thread après le thread.start() et avant de manipuler le résultat.

  6. #6
    Membre très actif
    Avatar de la.lune
    Homme Profil pro
    Directeur Technique
    Inscrit en
    Décembre 2010
    Messages
    548
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Comores

    Informations professionnelles :
    Activité : Directeur Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2010
    Messages : 548
    Par défaut
    Désolé je n'ai pas pris le temps d'aller voir l'exemple fourni dans le lien.

    Déjà même avec un seul Thread et il attend la fin du processus pour afficher rien empêche de faire ça dans un Thread séparé si notre application peut faire autre chose, en plus si le procesus se bloc pourquoi on doit être nous aussi bloqué, on lui laisse faire son boulot séparément.

    Là plutôt si tu as un seul thread et tu ne veux pas opter pour la solution prétendante il y a une solution plus simple , c'est d'introduire ton log dans la méthodes run dans le bloc finaly ou à la fin de la 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
    private static String result = "";
    public static void getTextOutput(final Process p) {
            new Thread() {
                public void run() {
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
                        String lineBuffered = "";
                        try {
                            result = reader.readLine();
                            while((lineBuffered = reader.readLine()) != null) {
                                result += "\n" + lineBuffered;
                            }
                        } finally {
                            reader.close();
                            monLog.log(Level.WARNING," TEXT " +  result );
                        }
                    } catch(IOException ioe) {
                        ioe.printStackTrace();
                    }
     
                }
            }.start();
     
    }

  7. #7
    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,

    Citation Envoyé par thelvin Voir le message
    Ok, mais dans l'exemple que tu donnes, il n'y a pas d'interaction (tu lis la sortie de l'autre processus, mais tu ne lui envoies rien du tout en entrée.) Il n'y a donc pas de risque d'interblocage.
    Le Thread ne sert à rien dans le cas présent...

    Déjà dès qu'on fait un thread pour attendre son résultat immédiatement après c'est qu'on a un problème de conception dans l'utilisation du thread.


    Toutefois si son traitement sur le process se limite à cela il y a quand même des risques d'interblocages s'il se contente de lire le flux de sortie !

    • Si le process attend des données sur stdin, il va attendre longtemps.
    • Si le process écrit beaucoup de données sur stderr, il peut remplir le buffer de communication avec l'appli Java, ce qui va lui provoquer un blocage I/O...


    Si un de ces deux cas arrive, alors le Thread sera lui aussi bloqué puisque la lecture attendra la fin du process, et le process attendra soit des données, soit que le flux de sortie soit lu.

    On peut affirmer que le process appelé ne fera jamais cela, mais on n'en est jamais sûr. Une configuration système ou autre peut provoquer l'affichage de message de confirmation ou activer des logs sut stderr...

    Si on veut ignorer ces flux, il faut les fermer explicitement via close(), ainsi dans le process fils :
    • Une lecture sur stdin provoquera un erreur (mais au moins cela ne devrait pas le bloquer).
    • Les écritures dans stderr seront perdus (envoi vers /dev/null) mais ne bloqueront pas.

    Plus de blocage !



    Sinon pour revenir il y a plusieurs chose qui me dérange :
    • Le "result" en static !!! Bref la méthode risque d'être inutilisable si on l'appelle plusieurs fois (surtout que tout est threadé dans ca risque d'être un beau bordel).
    • La lecture par ligne qui ne sert à rien : pourquoi faire un découpage par ligne si au final on veut récupérer le contenu complet du fichier ?
    • L'opérateur "+" (ou "+=") sur un String, qui est une vrai atrocité qui peut plomber les performances comme pas possible !



    Perso je modifierais la méthode comme ceci, afin de lire le contenu d'un flux :
    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
    	public static String readAsText(final InputStream input) throws IOException {
    		// Attention au Charset qui peut poser problème :
    		Reader reader = new InputStreamReader(input);
    		try {
    			StringBuilder sb = new StringBuilder();
    			char[] cbuf = new char[8192];
    			int len;
    			while ( (len=reader.read(cbuf)) > 0 ) {
    				sb.append(cbuf, 0, len);
    			}
    			return sb.toString();
    		} finally {
    			reader.close();
    		}
    	}

    Ensuite pour la lecture du process, si je veux uniquement le flux de sortie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	Process process = ...
    	try {
    		// On ferme le flux STDIN
    		process.getOutputStream().close();
    		// On ferme le flux STDERR :
    		process.getErrorStream().close();
    		// Puis on lit le résultat de STDOUT :
    		String result = readAsText(process.getInputStream());
    	} finally {
    		// garde-fou en cas d'erreur : on tue le process
    		process.destroy();
    	}
    A noter que depuis Java 5.0 on peut utiliser ProcessBuilder pour fusionner les flux stdout et stderr, ce qui permet de lire les deux en un seul flux.
    Et depuis Java 7 cette même classe permet de rediriger les I/O du process vers les I/O du programme Java...




    Maintenant si on doit traiter deux flux distinctement, il faut bien créer un thread pour que la lecture de chaque flux soit dans un thread séparé.
    Par exemple pour lire stdout et stderr dans deux variables différentes, on commencera par faire une méthode readAsText() qui fonctionnera en tâche de fond.
    Le plus simple étant d'utiliser FutureTask de Java 5.0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    	public static Future<String> readAsTextInBackground(final InputStream input) {
    		// 1. On crée une FutureTask avec notre traitement en tâche de fond :
    		FutureTask<String> task = new FutureTask<>(new Callable<String>() {
    			 @Override
    			public String call() throws Exception {
    				return readAsText(input);
    			}
    		});
    		// 2. On démarre un Thread avec cette tâche :
    		new Thread(task).start();
    		// 3. On retourne l'objet Future permettant de récupérer le résultat :
    		return task;
    	}
    Ce qui donne au final :
    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
    	Process process = null;
    	try {
    		// On ferme le flux STDIN
    		process.getOutputStream().close();
    		// On ferme le flux STDERR (en tâche de fond) :
    		Future<String> stderrFuture = readAsTextInBackground(process.getErrorStream());
    		// Puis on lit le résultat de STDOUT (bloquant) :
    		String stdout = readAsText(process.getInputStream());
     
    		// On récupère le resultat de la tâche de fond (bloquant) :
    		String stderr = stderrFuture.get();
     
    	} finally {
    		process.destroy();
    	}

    a++

  8. #8
    Membre averti
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    45
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 45
    Par défaut
    Merci pour vos réponses !

    En fait, ce que je fais :
    1. je crée un processus grâce au Runtime.getRuntime().exec("./Processus")

    2. J'initialise un tableau de String qui sont les noms de plusieurs fichiers et je parcours ce tableau.

    3. Je lis un fichier .txt pour avoir un texte

    4. Puis, j'envoie ce texte au processus grâce à OutputStreamWriter.
    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
     
    	public static void setTextInput(final Process p, final String linesToTurn) {
    		new Thread() {
    			public void run() {
    				BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
    				try {
    					writer.write(linesToTurn);
    					writer.flush();
    					writer.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}.start();
    	}
    5. Et, je lis la réponse que l'on me renvoit grâce à InputStreamReader.
    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 static String readAsText(final InputStream input) throws IOException {
    		// Attention au Charset qui peut poser problème :
    		Reader reader = new InputStreamReader(input);
    		try {
    			StringBuilder sb = new StringBuilder();
    			char[] cbuf = new char[8192];
    			int len;
    			while ( (len=reader.read(cbuf)) > 0 ) {
    				sb.append(cbuf, 0, len);
    			}
    			return sb.toString();
    		} finally {
    			reader.close();
    		}
    	}
    6. Et, je remet la réponse dans un autre fichier.

    7. Puis je recommence à l'étape 3 mais avec le fichier suivant de mon tableau.

    Au premier fichier, cela fonctionne mais pas pour les autres car cela est du au fait que à l'étape 4 et 5 je ferme les flux d'écriture et de lecture ce qui fait que le processus n'est plus en exécution.

    Du coup je suis obligée à chaque lecture d'un fichier (étape 3) je dois relancer le processus (étape 1), ce qui ralenti les performances de mon application. Y a t-il un moyen d'éviter de relancer mon processus?

    Par contre, si je ne ferme pas les flux, le code reste bloqué.

Discussions similaires

  1. problème d'une variable static
    Par salyiohh dans le forum Langage
    Réponses: 1
    Dernier message: 21/06/2015, 14h57
  2. problème lecture variable static d'un singleton
    Par totoscill dans le forum Langage
    Réponses: 8
    Dernier message: 28/07/2009, 08h21
  3. Problème avec une variable static
    Par Kevin12 dans le forum Général Java
    Réponses: 2
    Dernier message: 05/05/2008, 17h20
  4. Réponses: 6
    Dernier message: 12/09/2007, 17h31
  5. Variable static avec thread
    Par oxor3 dans le forum Threads & Processus
    Réponses: 7
    Dernier message: 27/08/2004, 11h45

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