Java: Hilos, Procesos y Control de Interrupciones en un Ejemplo de Carrera F1

Enviado por Chuletator online y clasificado en Informática y Telecomunicaciones

Escrito el en español con un tamaño de 10,52 KB

Resumen

Este documento contiene varios ejemplos en Java que muestran el uso de hilos (threads), control de interrupciones, manejo de procesos con ProcessBuilder, lectura de salida de procesos y redirección de salida a fichero. El ejemplo central simula una carrera de F1 donde se pueden interrumpir las escuderías desde un hilo de control.

Código corregido

A continuación se incluye el código corregido y formateado. Se han corregido errores ortográficos y gramaticales en los comentarios y en los mensajes impresos por pantalla. No se ha eliminado contenido; se han aclarado algunos puntos (por ejemplo, URL en curl) para evitar ambigüedades.

Importaciones

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Scanner;

Control (hilo de lectura desde teclado)

// Este hilo se dedica únicamente a leer del teclado la opción que pida el usuario
// para detener la escudería que solicite.
class Control extends Thread {
    ArrayList<CocheF1> escuderias;

    // Constructor
    public Control(ArrayList<CocheF1> escuderias) {
        this.escuderias = escuderias;
    }

    @Override
    public void run() {
        // Variables locales
        Scanner in = new Scanner(System.in);
        int escuderiaAParar;
        while (true) {
            // Pregunto al usuario
            System.out.println("Introduce una escudería para pararla (o bien, 0 para salir):");
            try {
                escuderiaAParar = Integer.parseInt(in.nextLine());
            } catch (NumberFormatException e) {
                System.out.println("Entrada no válida. Introduce un número.");
                continue;
            }
            // Si me piden 0, detengo todas las escuderías. Este hilo se parará automáticamente
            // porque ha sido declarado como daemon en el principal, así que no hace falta salir del while.
            if (escuderiaAParar == 0) {
                for (CocheF1 coche : escuderias) {
                    coche.interrupt();
                }
            } else {
                // Paro a la escudería que le toque, sabiendo que Ferrari está en la 0 en el array,
                // así que si me piden la 1, tiene que ser Ferrari (le resto 1).
                int index = escuderiaAParar - 1;
                if (index >= 0 && index < escuderias.size()) {
                    escuderias.get(index).interrupt();
                } else {
                    System.out.println("Número de escudería fuera de rango.");
                }
            }
        }
    }
}

Ejercicio1A

public class Ejercicio1A {
    public static void main(String[] args) {
        Scanner leer = new Scanner(System.in);
        ProcessBuilder pb;
        Process p;
        System.out.println("Introduzca la cantidad de veces a repetir el proceso");
        int numRep = leer.nextInt();
        pb = new ProcessBuilder("CMD", "/C", "ping -n " + numRep + " 8.8.8.8");
        try {
            p = pb.start();
            while (p.isAlive()) {
                System.out.println("Proceso principal (" + LocalTime.now() + ") - Proceso hijo no ha terminado. Esperando otro medio segundo.");
                Thread.sleep(500);
            }
            System.out.println("Proceso finalizado");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Ejercicio1B_2

public class Ejercicio1B_2 {
    public static void main(String[] args) {
        Scanner leer = new Scanner(System.in);
        ProcessBuilder pb;
        Process p;
        System.out.println("Introduce las reps");
        int reps = leer.nextInt();
        pb = new ProcessBuilder("CMD", "/C", "ping -n " + reps + " 8.8.8.8");
        try {
            p = pb.start();
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String linea;
            while ((linea = br.readLine()) != null) {
                System.out.println(linea);
                Thread.sleep(500);
            }
            System.out.println("El proceso padre ha finalizado");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Ejercicio1C_2

public class Ejercicio1C_2 {
    public static void main(String[] args) {
        Scanner leer = new Scanner(System.in);
        ProcessBuilder pb;
        Process p;
        System.out.println("Introduce reps");
        int rep = leer.nextInt();
        pb = new ProcessBuilder("CMD", "/C", "ping -n " + rep + " 8.8.8.8");
        pb.redirectOutput(new File("fichero.txt"));
        try {
            p = pb.start();
            ProcessHandle ph = p.toHandle();
            System.out.println("Proceso en ejecución y su PID es: " + ph.pid());
            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Ejercicio2_2

public class Ejercicio2_2 {
    private static final int pos_estado = 0;
    private static final int pos_pais = 1;
    private static final int pos_ciudad = 4;

    public static void main(String[] args) {
        Scanner leer = new Scanner(System.in);
        ProcessBuilder pb;
        Process p;
        System.out.println("Introduce una IP");
        String ip = leer.nextLine();
        // Se usa la URL base correctamente concatenada con la IP
        pb = new ProcessBuilder("CMD", "/C", "curl http://ip-api.com/csv/" + ip);
        try {
            p = pb.start();
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String linea = br.readLine();
            if (linea == null) {
                System.out.println("No se ha recibido respuesta.");
            } else {
                String[] palabras = linea.split(",");
                if (palabras.length > pos_ciudad && "success".equals(palabras[pos_estado])) {
                    System.out.println("Info encontrada, ciudad: " + palabras[pos_ciudad] + ", país: " + palabras[pos_pais]);
                } else {
                    System.out.println("No se ha encontrado información");
                }
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

CocheF1 (clase de hilo que representa un coche)

class CocheF1 extends Thread {
    private String nombre;
    private int distancia;
    ArrayList<String> podio;

    // Constructor
    public CocheF1(String nombre, int distancia, ArrayList<String> podio) {
        this.nombre = nombre;
        this.distancia = distancia;
        this.podio = podio;
    }

    // Run
    @Override
    public void run() {
        // Variables locales
        boolean solicitadoParar = false;
        // Recorro kilómetros e imprimo
        for (int i = 1; i <= distancia; i++) {
            // Compruebo si se ha solicitado interrumpir el hilo. Si se ha solicitado,
            // necesito alguna manera de detectarlo.
            if (this.isInterrupted()) {
                solicitadoParar = true;
                break;
            }
            // Informo
            System.out.println(this.nombre + " en km " + i);
            // Me espero
            try {
                Thread.sleep((int) (Math.random() * 401) + 300);
            } catch (InterruptedException e) {
                // En el caso de que cuando se solicite interrumpir el hilo este esté durmiendo,
                // saltará la excepción InterruptedException y hay que capturarla.
                // Es importante poner el break, porque la interrupción se limpiará y
                // se volverá a ejecutar de lo contrario.
                solicitadoParar = true;
                break;
            }
        }
        // Imprimo adecuadamente si llega a la meta o abandona
        if (solicitadoParar) {
            System.out.println("¡¡" + this.nombre + " abandona!!");
        } else {
            System.out.println(this.nombre + " llega a meta");
            podio.add(this.nombre);
        }
    }
}

F1 (clase principal que lanza la carrera)

public class F1 {
    public static void main(String[] args) {
        // Variables locales
        ArrayList<CocheF1> listaCoches = new ArrayList<>();
        ArrayList<String> podio = new ArrayList<>();
        Scanner in = new Scanner(System.in);
        int distancia;
        // Pregunto al usuario qué distancia hay que recorrer
        System.out.print("¿Cuántos km hay que recorrer?");
        distancia = Integer.parseInt(in.nextLine());
        // Ahora tengo que meter nuevos coches
        listaCoches.add(new CocheF1("Ferrari", distancia, podio));
        listaCoches.add(new CocheF1("McLaren", distancia, podio));
        listaCoches.add(new CocheF1("Red Bull", distancia, podio));
        listaCoches.add(new CocheF1("Mercedes", distancia, podio));
        // Premio a Ferrari
        listaCoches.get(0).setPriority(Thread.MAX_PRIORITY);
        // Los lanzo
        System.out.println("== INICIO DE LA CARRERA ==");
        for (CocheF1 coche : listaCoches) {
            coche.start();
        }
        // Inicio el hilo de control. Lo declaro como daemon para que se cierre cuando este hilo (principal)
        // también lo haga cuando termine.
        Thread control = new Control(listaCoches);
        control.setDaemon(true);
        control.start();
        // Me espero
        try {
            for (CocheF1 coche : listaCoches)
                coche.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // Anuncio el fin
        System.out.println("== FIN DE LA CARRERA ==");
        // Imprimo el podio
        System.out.print("== PODIO: ");
        for (int i = 0; i < 3 && i < podio.size(); i++) {
            System.out.print((i + 1) + "." + podio.get(i) + " ");
        }
        System.out.println(" ==");
    }
}

Notas

  • Se han añadido controles mínimos de validación de entrada en algunos puntos para evitar excepciones por formato incorrecto.
  • Se corrigieron errores ortográficos y se mejoró la claridad de los comentarios y mensajes por pantalla.
  • Para ejecutar correctamente algunos fragmentos en Windows se usa "CMD /C"; en sistemas UNIX es necesario adaptar los comandos (por ejemplo, usar "ping -c" en lugar de "ping -n").

Entradas relacionadas: