Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial programming >> Java

Dominando el multihilo en Java: Programación concurrente eficiente

Java es un lenguaje de programación multi-hilo, lo que significa que podemos desarrollar programas multi-hilo usando Java. Un programa multi-hilo contiene dos o más partes que pueden ejecutarse concurrentemente, y cada parte puede manejar una tarea diferente al mismo tiempo, aprovechando óptimamente los recursos disponibles, especialmente cuando tu computadora tiene múltiples CPUs.

Por definición, la multitarea es cuando múltiples procesos comparten recursos de procesamiento comunes, como una CPU. El multi-hilo extiende la idea de la multitarea a aplicaciones donde puedes subdividir operaciones específicas dentro de una sola aplicación en hilos individuales. Cada uno de los hilos puede ejecutarse en paralelo. El SO divide el tiempo de procesamiento no solo entre diferentes aplicaciones, sino también entre cada hilo dentro de una aplicación.

El multi-hilo te permite escribir código donde múltiples actividades pueden proceder concurrentemente en el mismo programa.

Ciclo de vida de un hilo

Un hilo pasa por varias etapas en su ciclo de vida. Por ejemplo, un hilo nace, se inicia, se ejecuta y luego muere. El siguiente diagrama muestra el ciclo de vida completo de un hilo.

Dominando el multihilo en Java: Programación concurrente eficiente

Las etapas del ciclo de vida son:

Prioridades de hilos

Cada hilo Java tiene una prioridad que ayuda al sistema operativo a determinar el orden en que se programan los hilos.

Las prioridades de hilos en Java van desde MIN_PRIORITY (constante 1) hasta MAX_PRIORITY (constante 10). Por defecto, a cada hilo se le da prioridad NORM_PRIORITY (constante 5).

Los hilos con mayor prioridad son más importantes para un programa y deberían recibir tiempo de procesador antes que los de menor prioridad. Sin embargo, las prioridades de hilos no garantizan el orden de ejecución y dependen mucho de la plataforma.

Crear un hilo implementando la interfaz Runnable

Si tu clase está destinada a ejecutarse como hilo, puedes lograrlo implementando la interfaz Runnable. Debes seguir tres pasos básicos:

Paso 1

Como primer paso, necesitas implementar el método run() proporcionado por la interfaz Runnable. Este método proporciona el punto de entrada para el hilo y colocarás toda tu lógica de negocio dentro de él. Sintaxis simple:

public void run()

Paso 2

Como segundo paso, instanciarás un objeto Thread usando el siguiente constructor:

Thread(Runnable threadObj, String threadName);

Donde threadObj es una instancia de una clase que implementa Runnable y threadName es el nombre dado al nuevo hilo.

Paso 3

Una vez creado el objeto Thread, puedes iniciarlo llamando al método start(), que ejecuta una llamada a run(). Sintaxis simple:

void start();

Ejemplo

Aquí hay un ejemplo que crea un nuevo hilo y lo inicia:

Demo en vivo
class RunnableDemo implements Runnable {
    private Thread t;
    private String threadName;
    RunnableDemo(String name) {
        threadName = name;
        System.out.println("Creando " + threadName);
    }
    public void run() {
        System.out.println("Ejecutando " + threadName);
        try {
            for(int i = 4; i > 0; i--) {
                System.out.println("Hilo: " + threadName + ", " + i);
                Thread.sleep(50);
            }
        } catch (InterruptedException e) {
            System.out.println("Hilo " + threadName + " interrumpido.");
        }
        System.out.println("Hilo " + threadName + " finalizando.");
    }
    public void start() {
        System.out.println("Iniciando " + threadName);
        if (t == null) {
            t = new Thread(this, threadName);
            t.start();
        }
    }
}
public class TestThread {
    public static void main(String args[]) {
        RunnableDemo R1 = new RunnableDemo("Hilo-1");
        R1.start();
        RunnableDemo R2 = new RunnableDemo("Hilo-2");
        R2.start();
    }
}

Esto producirá el siguiente resultado (el orden puede variar):

Salida

Creando Hilo-1
Iniciando Hilo-1
Creando Hilo-2
Iniciando Hilo-2
Ejecutando Hilo-1
Hilo: Hilo-1, 4
Ejecutando Hilo-2
Hilo: Hilo-2, 4
Hilo: Hilo-1, 3
Hilo: Hilo-2, 3
Hilo: Hilo-1, 2
Hilo: Hilo-2, 2
Hilo: Hilo-1, 1
Hilo: Hilo-2, 1
Hilo Hilo-1 finalizando.
Hilo Hilo-2 finalizando.

Crear un hilo extendiendo la clase Thread

La segunda forma es crear una nueva clase que extienda Thread usando dos pasos simples. Este enfoque ofrece más flexibilidad al manejar múltiples hilos usando los métodos disponibles en la clase Thread.

Paso 1

Debes sobrescribir el método run() disponible en la clase Thread. Este método proporciona el punto de entrada para el hilo. Sintaxis:

public void run()

Paso 2

Una vez creado el objeto Thread, lo inicias llamando a start():

void start();

Ejemplo

El programa anterior reescrito extendiendo Thread:

Demo en vivo
class ThreadDemo extends Thread {
    private Thread t;
    private String threadName;
    ThreadDemo(String name) {
        threadName = name;
        System.out.println("Creando " + threadName);
    }
    public void run() {
        System.out.println("Ejecutando " + threadName);
        try {
            for(int i = 4; i > 0; i--) {
                System.out.println("Hilo: " + threadName + ", " + i);
                Thread.sleep(50);
            }
        } catch (InterruptedException e) {
            System.out.println("Hilo " + threadName + " interrumpido.");
        }
        System.out.println("Hilo " + threadName + " finalizando.");
    }
    public void start() {
        System.out.println("Iniciando " + threadName);
        if (t == null) {
            t = new Thread(this, threadName);
            t.start();
        }
    }
}
public class TestThread {
    public static void main(String args[]) {
        ThreadDemo T1 = new ThreadDemo("Hilo-1");
        T1.start();
        ThreadDemo T2 = new ThreadDemo("Hilo-2");
        T2.start();
    }
}

Produce el mismo resultado que el ejemplo anterior.

Métodos de la clase Thread

A continuación se muestra una lista de métodos importantes disponibles en la clase Thread.

NºMétodo y Descripción
1

public void start()

Inicia el hilo en una ruta de ejecución separada e invoca el método run() en este objeto Thread.

2

public void run()

Si este objeto Thread se instanció usando un objetivo Runnable separado, se invoca run() en ese objeto Runnable.

3

public final void setName(String name)

Cambia el nombre del objeto Thread. También existe getName() para recuperarlo.

4

public final void setPriority(int priority)

Establece la prioridad de este objeto Thread. Valores posibles entre 1 y 10.

5

public final void setDaemon(boolean on)

Un parámetro true denota este hilo como daemon.

6

public final void join(long millisec)

El hilo actual invoca este método en un segundo hilo, bloqueándose hasta que el segundo termine o pasen los milisegundos especificados.

7

public void interrupt()

Interrumpe este hilo, causando que continúe si estaba bloqueado.

8

public final boolean isAlive()

Devuelve true si el hilo está vivo (después de start() y antes de terminar).

Los métodos anteriores se invocan en un objeto Thread particular. Los siguientes son métodos estáticos que operan sobre el hilo en ejecución actual:

NºMétodo y Descripción
1

public static void yield()

Hace que el hilo actual ceda el paso a otros hilos de misma prioridad que esperan ser planificados.

2

public static void sleep(long millisec)

Bloquea el hilo actual por al menos los milisegundos especificados.

3

public static boolean holdsLock(Object x)

Devuelve true si el hilo actual posee el bloqueo del objeto dado.

4

public static Thread currentThread()

Devuelve una referencia al hilo en ejecución actual.

5

public static void dumpStack()

Imprime el rastreo de pila del hilo actual, útil para depurar aplicaciones multi-hilo.

Ejemplo

El siguiente programa ThreadClassDemo demuestra algunos de estos métodos. Considera una clase DisplayMessage que implementa Runnable:

// Nombre del archivo: DisplayMessage.java
public class DisplayMessage implements Runnable {
    private String message;
    public DisplayMessage(String message) { this.message = message; }
    public void run() {
        while(true) { System.out.println(message); }
    }
}

Otra clase que extiende Thread:

// Nombre del archivo: GuessANumber.java
public class GuessANumber extends Thread {
    private int number;
    public GuessANumber(int number) { this.number = number; }
    public void run() {
        int counter = 0, guess = 0;
        do {
            guess = (int) (Math.random() * 100 + 1);
            System.out.println(this.getName() + " adivina " + guess);
            counter++;
        } while(guess != number);
        System.out.println("** ¡Correcto! " + this.getName() + " en " + counter + " intentos.**");
    }
}

Programa principal que usa las clases anteriores:

// Nombre del archivo: ThreadClassDemo.java
public class ThreadClassDemo {
    public static void main(String[] args) {
        Runnable hello = new DisplayMessage("Hola");
        Thread thread1 = new Thread(hello);
        thread1.setDaemon(true);
        thread1.setName("hola");
        System.out.println("Iniciando hilo hola...");
        thread1.start();
        Runnable bye = new DisplayMessage("Adiós");
        Thread thread2 = new Thread(bye);
        thread2.setPriority(Thread.MIN_PRIORITY);
        thread2.setDaemon(true);
        System.out.println("Iniciando hilo adiós...");
        thread2.start();
        System.out.println("Iniciando hilo3...");
        Thread thread3 = new GuessANumber(27);
        thread3.start();
        try { thread3.join(); } catch (InterruptedException e) { System.out.println("Hilo interrumpido."); }
        System.out.println("Iniciando hilo4...");
        Thread thread4 = new GuessANumber(75);
        thread4.start();
        System.out.println("main() finalizando...");
    }
}

Resultado típico (varía en cada ejecución):

Salida

Iniciando hilo hola...
Iniciando hilo adiós...
Hola
Hola
Hola
Hola
Hola
Hola
Adiós
Adiós
Adiós
Adiós
Adiós
.......

Conceptos principales de multihilo en Java

Al programar multihilo en Java, necesitarás dominar los siguientes conceptos:

Java

  1. Dominar la multihilo en C++: Programación concurrente eficiente
  2. Dominar Operadores en Java: Tipos, Sintaxis y Ejemplos Prácticos
  3. Comentarios en Java: Tipos, Uso y Mejores Prácticas
  4. Dominar el Bucle for Mejorado en Java: Sintaxis, Ejemplos y Mejores Prácticas
  5. Dominar Strings en Java: Creación, Métodos y Mejores Prácticas
  6. Dominando interfaces en Java: Conceptos, implementación y mejores prácticas
  7. Dominando las clases internas anónimas en Java: Definición, sintaxis y ejemplos prácticos
  8. Dominando Try‑with‑Resources en Java: Gestión automática de recursos explicada
  9. Anotaciones en Java explicadas: Tipos, ubicación y ejemplos prácticos
  10. Aserciones en Java: Guía práctica para detectar bugs y asegurar fiabilidad del código
  11. Entendiendo Java Vector: Uso, Características y Comparación con ArrayList