CodeGym /Cursos /JAVA 25 SELF /Análisis de errores típicos al trabajar con procesos

Análisis de errores típicos al trabajar con procesos

JAVA 25 SELF
Nivel 61, Lección 4
Disponible

1. Deadlock al leer/escribir flujos

Uno de los errores más desagradables es cuando un proceso se cuelga y no termina, aunque parece que todo está bien. Una causa frecuente es el desbordamiento del búfer de salida o los errores del proceso externo. Si no lees ambos flujos (stdout y stderr), el proceso puede «bloquearse» al intentar escribir, porque nadie consume los datos de su búfer.

Ejemplo del problema

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

Si el comando escribe algo en stderr (por ejemplo, ¡java -version casi siempre escribe ahí!), ese flujo se llena y el proceso se bloquea.

Cómo hacerlo bien

Lee ambos flujos en paralelo (mediante hilos separados o ExecutorService):

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

// Leemos 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();
    }
});

// Leemos 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();

Conclusión:
Si no lees stderr — el proceso puede colgarse.
Si no lees stdout — también puede colgarse.
Lee ambos flujos y serás feliz.

2. Problemas con la codificación

Los procesos externos pueden usar una codificación distinta para su salida de texto que la que tu aplicación Java usa por defecto. Si no especificas la codificación correcta, obtendrás «caracteres ilegibles» en lugar de texto (especialmente notable con el cirílico).

Ejemplo de error

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

Este código usa la codificación del sistema por defecto. Pero si el proceso externo escribe, por ejemplo, en UTF-8 y el tuyo está en Windows-1251, el texto en cirílico se convertirá en galimatías.

Cómo hacerlo bien

Indica explícitamente la codificación necesaria si la conoces:

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

Si no estás seguro, consulta la documentación del programa o prueba varias opciones.

Truco: en Windows, las utilidades de consola suelen usar CP866 o Windows-1251, y en Linux — UTF-8.

3. Diferencias entre plataformas

Los comandos que funcionan en un sistema operativo pueden no existir en otro. Por ejemplo, ls está en Linux/Mac, pero no en Windows (allí — dir). También difieren la sintaxis de los comandos, los separadores de rutas y las comillas.

Ejemplo de error

ProcessBuilder builder = new ProcessBuilder("ls", "-l");
builder.start(); // En Windows: "ls" no se encuentra!

Cómo hacerlo bien

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");
}

Ruta de archivos: usa File.separator en lugar de «/» o «\», para evitar problemas con las rutas.

4. Problemas de permisos

Algunos comandos requieren privilegios de administrador (eliminar archivos del sistema, cambiar la configuración de red, etc.). Si faltan permisos, el comando terminará con error o ni siquiera se iniciará.

Ejemplo

ProcessBuilder builder = new ProcessBuilder("rm", "-rf", "/root/secret.txt");
Process process = builder.start();
// ... esperamos el error Permission denied

Cómo hacerlo bien

  • Comprueba si tu comando necesita privilegios elevados.
  • Gestiona el código de retorno del proceso con process.exitValue().
  • Lee stderr: allí suele estar la causa del error (por ejemplo, «Permission denied»).

5. Fugas de recursos

Si no cierras los flujos del proceso (InputStream, OutputStream, ErrorStream), pueden quedarse «colgados», consumir recursos e incluso bloquear la finalización. Del mismo modo, si no terminas el propio proceso, puede convertirse en un «zombi» en el sistema.

Ejemplo de error

Process process = builder.start();
// ... trabajamos, pero no cerramos los flujos!

Cómo hacerlo bien

Usa try-with-resources para los flujos:

try (BufferedReader reader = new BufferedReader(
         new InputStreamReader(process.getInputStream()))) {
    // Leemos la salida
}

Tras terminar con el proceso, deténlo correctamente:

process.destroy(); // Finaliza el proceso (si sigue vivo)

Atención: si no cierras los flujos, pueden producirse fugas de memoria, bloqueos y problemas del sistema (especialmente al lanzar muchos procesos).

6. Deadlock en la comunicación interactiva

En el intercambio interactivo es fácil caer en una situación de interbloqueo: Java espera una respuesta y el proceso externo espera tus datos. Al final, ambos «guardan silencio». O bien envías un mensaje pero no lees la respuesta: en algún momento se llena el búfer del programa externo y deja de escribir.

Para evitarlo, divide responsabilidades: un hilo se encarga de leer y otro — de escribir. Así la comunicación avanza en paralelo y nadie bloquea a nadie. Además, no dejes flujos abiertos tras finalizar: ciérralos para que el sistema no retenga recursos innecesarios.

1
Tarea
JAVA 25 SELF, nivel 61, lección 4
Bloqueada
Intrusión en la Zona Protegida 🚫
Intrusión en la Zona Protegida 🚫
1
Tarea
JAVA 25 SELF, nivel 61, lección 4
Bloqueada
El Ojo Omnisciente del Detective 🕵️
El Ojo Omnisciente del Detective 🕵️
1
Cuestionario/control
Trabajo con procesos, nivel 61, lección 4
No disponible
Trabajo con procesos
Trabajo con procesos
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION