CodeGym /Cours /JAVA 25 SELF /Gestion du cycle de vie d’un processus

Gestion du cycle de vie d’un processus

JAVA 25 SELF
Niveau 61 , Leçon 2
Disponible

1. Attendre la fin d’un processus : waitFor()

Lorsque vous lancez un processus externe depuis Java, il vit sa propre vie : il calcule des choses, écrit dans la console, parfois se bloque (coucou, ping avec un million de paquets). Mais parfois, nous devons savoir quand ce processus s’est terminé, comment il s’est terminé (succès ou échec) et — si quelque chose tourne mal — être capables de le « tuer ». Bref, comme dans la vraie vie : si vous avez confié une tâche à quelqu’un, il est bon de savoir s’il a réussi, et d’avoir la possibilité de l’arrêter s’il se met soudain à faire du café sans fin.

Quand vous lancez un processus via ProcessBuilder, vous obtenez un objet de type Process. Cet objet agit comme une télécommande : grâce à lui, vous pouvez attendre la fin du processus, obtenir le code de sortie, terminer le processus, ainsi que gérer les flux d’entrée/sortie.

Comment attendre la fin d’un processus ?

Pour cela, il existe la méthode waitFor() :

ProcessBuilder builder = new ProcessBuilder("ping", "google.com", "-c", "3");
Process process = builder.start();

System.out.println("Nous attendons la fin du processus...");
int exitCode = process.waitFor(); // Bloque le thread courant jusqu’à la fin du processus
System.out.println("Le processus s’est terminé avec le code : " + exitCode);

Que se passe-t-il ?

  • Le programme lance un processus externe (ping).
  • Ensuite, le thread dans lequel waitFor() est appelé « s’endort » jusqu’à ce que le processus externe se termine.
  • Dès que le processus externe a fini son travail, waitFor() renvoie le code de sortie (généralement 0 — succès, non 0 — erreur).

Comment connaître le code de sortie ?

La méthode waitFor() renvoie ce code. Vous pouvez également l’obtenir séparément via exitValue() :

int code = process.exitValue();

Mais faites attention : si le processus n’est pas encore terminé, l’appel à exitValue() lèvera l’exception IllegalThreadStateException. C’est pourquoi, en général, on attend d’abord la fin via waitFor(), puis on regarde le code.

2. Interrompre un processus : destroy() et destroyForcibly()

Parfois, un processus externe dure trop longtemps ou se bloque. Par exemple, quelqu’un a lancé ping avec 1000 paquets alors que vous avez un deadline dans 5 secondes. Il faut un « bouton rouge ».

Arrêt en douceur : destroy()

La méthode destroy() envoie au processus un signal de terminaison (SIGTERM sous Unix, CTRL-BREAK sous Windows), et le processus a une chance de se terminer correctement.

Process process = new ProcessBuilder("ping", "google.com").start();

Thread.sleep(2000); // Laisser tourner 2 secondes
process.destroy(); // Demander l’arrêt

Arrêt forcé : destroyForcibly()

Si le processus ne répond pas aux demandes polies, vous pouvez utiliser la méthode destroyForcibly(). C’est comme arracher la prise.

process.destroyForcibly();

Important : Après avoir appelé destroy() ou destroyForcibly(), il faut attendre la fin du processus via waitFor(). Parfois, le processus a besoin de temps pour « prendre conscience » de sa mort.

3. Attendre un processus avec délai d’attente

Depuis Java 8+, une méthode avec délai d’attente est disponible :

boolean finished = process.waitFor(5, java.util.concurrent.TimeUnit.SECONDS);
if (finished) {
    System.out.println("Le processus s’est terminé à temps !");
} else {
    System.out.println("Le processus ne s’est pas terminé, on l’arrête...");
    process.destroy();
}
  • Si le processus s’est terminé en 5 secondes, la méthode renvoie true.
  • Sinon — elle renvoie false, et vous pouvez agir (par exemple, arrêter le processus).

Exemple : lançons une commande longue et essayons de la « tuer » :

ProcessBuilder builder = new ProcessBuilder("ping", "google.com", "-c", "10");
Process process = builder.start();

boolean finished = process.waitFor(2, java.util.concurrent.TimeUnit.SECONDS);
if (!finished) {
    System.out.println("Le processus tourne trop longtemps, on l’arrête...");
    process.destroy();
    // On peut attendre la fin complète
    process.waitFor();
}
System.out.println("Code de sortie : " + process.exitValue());

4. Pratique : mini-outil pour lancer une commande avec délai d’attente

Écrivons un petit programme qui lance une commande externe avec un délai d’attente, attend sa fin et, en cas de dépassement, termine le processus.

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class ProcessTimeoutDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        Scanner scanner = new Scanner(System.in);

        System.out.println("Saisissez la commande à lancer (par ex., ping google.com):");
        String commandLine = scanner.nextLine();
        String[] command = commandLine.split(" ");

        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = builder.start();

        System.out.println("Combien de secondes faut-il attendre la fin ?");
        int timeout = scanner.nextInt();

        boolean finished = process.waitFor(timeout, TimeUnit.SECONDS);
        if (finished) {
            System.out.println("Le processus s’est terminé tout seul. Code de sortie : " + process.exitValue());
        } else {
            System.out.println("Temps écoulé ! Nous arrêtons le processus...");
            process.destroy();
            process.waitFor();
            System.out.println("Le processus a été interrompu de force. Code de sortie : " + process.exitValue());
        }
    }
}

Explication :

  • L’utilisateur saisit la commande à exécuter.
  • Le programme attend le délai indiqué.
  • Si le processus ne s’est pas terminé — il l’arrête.

Astuce : Sous Windows, ping envoie 4 requêtes par défaut, alors que sous Linux — indéfiniment. Pour Linux/Mac, ajoutez le paramètre -c 5 (par exemple, ping google.com -c 5).

5. Récupérer le code de sortie : qu’est-ce que c’est et à quoi ça sert ?

Dans le monde des lignes de commande, la règle est la suivante : si la commande s’est terminée avec succès, elle renvoie le code 0. Si quelque chose s’est mal passé — n’importe quel autre code.

  • Dans les systèmes de type Unix : 0 = succès, 1 et plus = erreur.
  • Sous Windows — à peu près pareil.

Vous pouvez utiliser ce code pour prendre des décisions dans votre programme Java :

int exitCode = process.waitFor();
if (exitCode == 0) {
    System.out.println("Réussi !");
} else {
    System.out.println("Erreur ! Code : " + exitCode);
}

6. Arrêter les processus bloqués

Parfois, un processus peut simplement se figer — par exemple, il attend une saisie de votre part, ou ne peut pas se connecter au réseau. Si vous l’ignorez, il restera en mémoire et consommera tranquillement des ressources.

Pour éviter cela, il est utile de lui fixer une limite de patience. La méthode waitFor(timeout, unit) permet d’attendre sa fin pendant un temps déterminé seulement. S’il n’a pas fini — arrêtez-le sans hésiter.

Après avoir appelé destroy(), il ne suffit pas de s’en laver les mains : assurez-vous que le processus est vraiment parti. Pour cela, on appelle à nouveau waitFor(). Et si cela ne suffit toujours pas — il reste l’ultime recours, destroyForcibly(). Elle interrompt tout sans autre forme de procès.

Exemple avec double tentative d’arrêt

boolean finished = process.waitFor(5, TimeUnit.SECONDS);
if (!finished) {
    process.destroy();
    if (!process.waitFor(2, TimeUnit.SECONDS)) {
        process.destroyForcibly();
    }
}

Particularités de l’API Process : points importants à retenir

Quand un processus s’est terminé, ne l’oubliez pas trop vite. Jetez un œil à sa sortie — la normale et celle des erreurs. On peut y trouver des informations importantes : message d’échec, avertissement, ou simplement la confirmation que tout s’est bien passé.

Ensuite, fermez impérativement tous les flux associés — InputStream, OutputStream et ErrorStream. C’est comme éteindre la lumière en partant : cela économise des ressources et maintient l’ordre dans le système.

Et souvenez-vous : si vous avez arrêté le processus de force, il se peut qu’il n’ait pas eu le temps d’écrire sa sortie correctement. Rien de dramatique — c’est un comportement normal dans ce genre de situations.

7. Exemple : lancer une commande longue et l’arrêter

Supposons que nous voulions lancer un processus long (par exemple, ping avec un grand nombre de paquets) et l’arrêter au bout de 3 secondes.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;

public class KillLongProcessDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        String[] command = {"ping", "google.com"};
        // Pour Linux/Mac : {"ping", "google.com", "-c", "100"}
        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = builder.start();

        // Lire la sortie du processus dans un thread séparé
        Thread readerThread = new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                // On ignore
            }
        });
        readerThread.start();

        // On attend 3 secondes
        if (!process.waitFor(3, TimeUnit.SECONDS)) {
            System.out.println("Le processus tourne trop longtemps, on l’arrête...");
            process.destroy();
            process.waitFor();
        }
        readerThread.join(); // Attendre la lecture de la sortie
        System.out.println("Le processus est terminé. Code de sortie : " + process.exitValue());
    }
}

8. Erreurs courantes lors de la gestion des processus

Erreur n° 1 : ne pas attendre la fin du processus. Si vous avez lancé un processus et l’avez simplement oublié (sans appeler waitFor()), le processus peut rester « en suspens » dans le système même après la fin de votre programme Java. Les flux d’entrée/sortie peuvent ne pas être fermés, ce qui entraînera des fuites de mémoire et de descripteurs de fichiers.

Erreur n° 2 : arrêter incorrectement un processus. Vous avez appelé destroy(), mais vous n’avez pas attendu la fin via waitFor(). Au final, le processus peut rester « zombie » (surtout sur les systèmes Unix).

Erreur n° 3 : attendre le code de sortie avant la fin du processus. Appeler exitValue() avant la fin du processus provoque l’exception IllegalThreadStateException.

Erreur n° 4 : ne pas gérer les exceptions. Les méthodes de travail avec les processus peuvent lever IOException et InterruptedException. N’oubliez pas de les intercepter ou de les déclarer dans la signature de la méthode.

Erreur n° 5 : ne pas lire la sortie du processus. Si vous ne lisez pas stdout et stderr, le processus peut se bloquer à cause d’un tampon de sortie saturé.

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION