CodeGym /Kursy /JAVA 25 SELF /Przegląd typowych błędów przy pracy z procesami

Przegląd typowych błędów przy pracy z procesami

JAVA 25 SELF
Poziom 61 , Lekcja 4
Dostępny

1. Deadlock przy odczycie/zapisie strumieni

Jednym z najbardziej dokuczliwych błędów jest sytuacja, gdy proces się zawiesza i nie kończy, choć wydaje się, że wszystko zrobiono poprawnie. Częsty powód — przepełnienie bufora wyjścia albo błędy procesu zewnętrznego. Jeśli nie czytać obu strumieni (stdout i stderr), proces może się „zablokować” przy próbie wypisania czegokolwiek, ponieważ nikt nie odbiera danych z jego bufora.

Przykład problemu

ProcessBuilder builder = new ProcessBuilder("java", "-version");
Process process = builder.start();
// Czytamy tylko stdout, a stderr ignorujemy!
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
process.waitFor();

Jeśli polecenie zapisuje coś do stderr (np. java -version prawie zawsze tam pisze!), ten strumień się przepełni i proces się zawiesi.

Jak zrobić to poprawnie

Czytaj oba strumienie równolegle (za pomocą oddzielnych wątków lub ExecutorService):

ProcessBuilder builder = new ProcessBuilder("java", "-version");
Process process = builder.start();

// Czytamy stdout
Thread stdoutThread = new Thread(() -> {
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream()))) {
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println("[stdout] " + line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
});

// Czytamy stderr
Thread stderrThread = new Thread(() -> {
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getErrorStream()))) {
        String line;
        while ((line = reader.readLine()) != null) {
            System.err.println("[stderr] " + line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
});

stdoutThread.start();
stderrThread.start();

process.waitFor();
stdoutThread.join();
stderrThread.join();

Wniosek:
Jeśli nie czytać stderr — proces może się zawiesić.
Jeśli nie czytać stdout — również może się zawiesić.
Czytaj oba strumienie — i kłopot zniknie!

2. Problemy z kodowaniem znaków

Procesy zewnętrzne mogą używać innego kodowania tekstu niż Twoja aplikacja Java domyślnie. Jeśli nie podasz właściwego kodowania, dostaniesz „krzaczki” zamiast tekstu (szczególnie widoczne przy cyrylicy).

Przykład błędu

BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

Ten kod używa systemowego kodowania domyślnego. Jeśli jednak proces zewnętrzny wypisuje np. w UTF-8, a u Ciebie jest Windows-1251, cyrylica zamieni się w „krzaczki”.

Jak zrobić to poprawnie

Przekazuj jawnie właściwe kodowanie, jeśli je znasz:

BufferedReader reader = new BufferedReader(
    new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
);

Jeśli nie masz pewności — zajrzyj do dokumentacji programu albo spróbuj kilku wariantów.

Wskazówka: w Windows narzędzia konsolowe często używają CP866 lub Windows-1251, a w Linuksie — UTF-8.

3. Różnice między platformami

Polecenia działające w jednym systemie operacyjnym mogą nie istnieć w innym. Na przykład ls jest w Linux/Mac, ale nie w Windows (tam — dir). Różni się składnia poleceń, separatory ścieżek i cudzysłowy.

Przykład błędu

ProcessBuilder builder = new ProcessBuilder("ls", "-l");
builder.start(); // W Windows: "ls" nie znaleziono!

Jak zrobić to poprawnie

String os = System.getProperty("os.name").toLowerCase();
ProcessBuilder builder;
if (os.contains("win")) {
    builder = new ProcessBuilder("cmd", "/c", "dir");
} else {
    builder = new ProcessBuilder("ls", "-l");
}

Ścieżki do plików: używaj File.separator zamiast „/” lub „\”, aby nie wpaść w kłopoty ze ścieżkami.

4. Problemy z uprawnieniami

Niektóre polecenia wymagają uprawnień administratora (usuwanie plików systemowych, zmiana ustawień sieci itp.). Przy braku uprawnień polecenie zakończy się błędem albo w ogóle się nie uruchomi.

Przykład

ProcessBuilder builder = new ProcessBuilder("rm", "-rf", "/root/secret.txt");
Process process = builder.start();
// ... oczekujemy błędu Permission denied

Jak zrobić to poprawnie

  • Sprawdzaj, czy Twoje polecenie wymaga podwyższonych uprawnień.
  • Obsługuj kod wyjścia procesu przez process.exitValue().
  • Czytaj stderr — tam najczęściej jest przyczyna błędu (np. „Permission denied”).

5. Wycieki zasobów

Jeśli nie zamykać strumieni procesu (InputStream, OutputStream, ErrorStream), mogą „wisieć”, zajmować zasoby, a nawet blokować zakończenie. Podobnie, jeśli nie zakończyć samego procesu, może stać się „zombie” w systemie.

Przykład błędu

Process process = builder.start();
// ... pracujemy, ale nie zamykamy strumieni!

Jak zrobić to poprawnie

Używaj try-with-resources dla strumieni:

try (BufferedReader reader = new BufferedReader(
         new InputStreamReader(process.getInputStream()))) {
    // Czytamy wyjście
}

Po zakończeniu pracy z procesem poprawnie go zatrzymuj:

process.destroy(); // Zakończ proces (jeśli wciąż żyje)

Uwaga: jeśli nie zamkniesz strumieni — możliwe są wycieki pamięci, zawieszenia i problemy systemowe (zwłaszcza przy masowym uruchamianiu procesów).

6. Deadlock przy interaktywnej komunikacji

Przy interaktywnej wymianie danych łatwo trafić na zakleszczenie: Java czeka na odpowiedź, a proces zewnętrzny — na Twoje dane. W efekcie obie strony „milczą”. Albo wysłałeś komunikat, ale nie czytasz odpowiedzi — w pewnym momencie programowi zewnętrznemu przepełni się bufor i przestanie pisać.

Aby tego uniknąć, rozdzielaj odpowiedzialności: jeden wątek odpowiada za odczyt, drugi — za zapis. Dzięki temu komunikacja przebiega równolegle i nikt nikogo nie blokuje. Nie zostawiaj też otwartych strumieni po zakończeniu — zamknij je, aby system nie trzymał zbędnych zasobów.

1
Ankieta/quiz
Praca z procesami, poziom 61, lekcja 4
Niedostępny
Praca z procesami
Praca z procesami
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION