Java: subprocesos múltiples

Java es un lenguaje de programación de múltiples subprocesos, lo que significa que podemos desarrollar un programa de múltiples subprocesos utilizando Java. Un programa de subprocesos múltiples contiene dos o más partes que pueden ejecutarse simultáneamente y cada parte puede manejar una tarea diferente al mismo tiempo haciendo un uso óptimo de los recursos disponibles especialmente cuando su computadora tiene múltiples CPU.

Por definición, la multitarea es cuando varios procesos comparten recursos de procesamiento comunes, como una CPU. El subproceso múltiple extiende la idea de la multitarea a aplicaciones en las que puede subdividir operaciones específicas dentro de una sola aplicación en subprocesos individuales. Cada uno de los subprocesos se puede ejecutar en paralelo. El sistema operativo divide el tiempo de procesamiento no solo entre diferentes aplicaciones, sino también entre cada hilo dentro de una aplicación.

El subproceso múltiple le permite escribir de una manera en la que pueden realizarse múltiples actividades simultáneamente 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.

Las siguientes son las etapas del ciclo de vida:

  • New- Un nuevo hilo comienza su ciclo de vida en el nuevo estado. Permanece en este estado hasta que el programa inicia el hilo. También se conoce comoborn thread.

  • Runnable- Después de que se inicia un hilo recién nacido, el hilo se vuelve ejecutable. Se considera que un subproceso en este estado está ejecutando su tarea.

  • Waiting- A veces, un hilo pasa al estado de espera mientras el hilo espera a que otro hilo realice una tarea. Un subproceso regresa al estado ejecutable solo cuando otro subproceso indica al subproceso en espera que continúe ejecutándose.

  • Timed Waiting- Un subproceso ejecutable puede entrar en el estado de espera temporizada durante un intervalo de tiempo especificado. Un hilo en este estado vuelve al estado ejecutable cuando ese intervalo de tiempo expira o cuando ocurre el evento que está esperando.

  • Terminated (Dead) - Un subproceso ejecutable entra en el estado terminado cuando completa su tarea o termina.

Prioridades de hilo

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

Las prioridades de los hilos de Java están en el rango entre MIN_PRIORITY (una constante de 1) y MAX_PRIORITY (una constante de 10). De forma predeterminada, cada hilo tiene prioridad NORM_PRIORITY (una constante de 5).

Los subprocesos con mayor prioridad son más importantes para un programa y deben asignarse tiempo de procesador antes que los subprocesos de menor prioridad. Sin embargo, las prioridades de los subprocesos no pueden garantizar el orden en el que se ejecutan los subprocesos y dependen en gran medida de la plataforma.

Crear un hilo implementando una interfaz ejecutable

Si su clase está destinada a ejecutarse como un hilo, puede lograrlo implementando un Runnableinterfaz. Deberá seguir tres pasos básicos:

Paso 1

Como primer paso, debe implementar un método run () proporcionado por un Runnableinterfaz. Este método proporciona un punto de entrada para el hilo y pondrá su lógica empresarial completa dentro de este método. A continuación se muestra una sintaxis simple del método run ():

public void run( )

Paso 2

Como segundo paso, creará una instancia Thread objeto usando el siguiente constructor -

Thread(Runnable threadObj, String threadName);

Donde, threadObj es una instancia de una clase que implementa elRunnable interfaz y threadName es el nombre que se le da al nuevo hilo.

Paso 3

Una vez que se crea un objeto Thread, puede iniciarlo llamando start()método, que ejecuta una llamada al método run (). A continuación se muestra una sintaxis simple del método start ():

void start();

Ejemplo

Aquí hay un ejemplo que crea un nuevo hilo y comienza a ejecutarlo:

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

Esto producirá el siguiente resultado:

Salida

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Crear un hilo ampliando una clase de hilo

La segunda forma de crear un hilo es crear una nueva clase que se extienda Threadclase usando los siguientes dos sencillos pasos. Este enfoque proporciona más flexibilidad en el manejo de varios subprocesos creados con los métodos disponibles en la clase Thread.

Paso 1

Necesitarás anular run( )método disponible en la clase Thread. Este método proporciona un punto de entrada para el hilo y pondrá su lógica empresarial completa dentro de este método. A continuación se muestra una sintaxis simple del método run ():

public void run( )

Paso 2

Una vez que se crea el objeto Thread, puede iniciarlo llamando start()método, que ejecuta una llamada al método run (). A continuación se muestra una sintaxis simple del método start ():

void start( );

Ejemplo

Aquí está el programa anterior reescrito para extender el hilo:

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

Esto producirá el siguiente resultado:

Salida

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Métodos de hilo

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

No Señor. Método y descripción
1

public void start()

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

2

public void run()

Si se creó una instancia de este objeto Thread utilizando un objetivo Runnable independiente, se invoca el método run () en ese objeto Runnable.

3

public final void setName(String name)

Cambia el nombre del objeto Thread. También hay un método getName () para recuperar el nombre.

4

public final void setPriority(int priority)

Establece la prioridad de este objeto Thread. Los valores posibles están entre 1 y 10.

5

public final void setDaemon(boolean on)

Un parámetro de verdadero denota este hilo como un hilo de demonio.

6

public final void join(long millisec)

El subproceso actual invoca este método en un segundo subproceso, lo que hace que el subproceso actual se bloquee hasta que el segundo subproceso termine o pase el número especificado de milisegundos.

7

public void interrupt()

Interrumpe este hilo, haciendo que continúe la ejecución si fue bloqueado por cualquier motivo.

8

public final boolean isAlive()

Devuelve verdadero si el hilo está vivo, que es en cualquier momento después de que el hilo se haya iniciado pero antes de que se complete.

Los métodos anteriores se invocan en un objeto Thread en particular. Los siguientes métodos de la clase Thread son estáticos. Invocar uno de los métodos estáticos realiza la operación en el subproceso que se está ejecutando actualmente.

No Señor. Método y descripción
1

public static void yield()

Hace que el subproceso que se está ejecutando actualmente ceda a cualquier otro subproceso de la misma prioridad que esté esperando ser programado.

2

public static void sleep(long millisec)

Hace que el subproceso que se está ejecutando actualmente se bloquee durante al menos el número especificado de milisegundos.

3

public static boolean holdsLock(Object x)

Devuelve verdadero si el hilo actual mantiene el bloqueo en el Objeto dado.

4

public static Thread currentThread()

Devuelve una referencia al hilo que se está ejecutando actualmente, que es el hilo que invoca este método.

5

public static void dumpStack()

Imprime el seguimiento de la pila para el subproceso que se está ejecutando actualmente, lo que es útil al depurar una aplicación multiproceso.

Ejemplo

El siguiente programa ThreadClassDemo muestra algunos de estos métodos de la clase Thread. Considere una claseDisplayMessage que implementa Runnable -

// File Name : DisplayMessage.java
// Create a thread to implement Runnable

public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}

A continuación se muestra otra clase que extiende la clase Thread:

// File Name : GuessANumber.java
// Create a thread to extentd Thread

public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

A continuación se muestra el programa principal, que hace uso de las clases definidas anteriormente:

// File Name : ThreadClassDemo.java
public class ThreadClassDemo {

   public static void main(String [] args) {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
      
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();

      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      } catch (InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}

Esto producirá el siguiente resultado. Puede probar este ejemplo una y otra vez y obtendrá un resultado diferente cada vez.

Salida

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

Principales conceptos de Java Multithreading

Mientras realiza la programación de subprocesos múltiples en Java, debe tener muy a mano los siguientes conceptos: