Imprima números pares e impares con 2 hilos

1. Introducción

En este tutorial, veremos cómo podemos imprimir números pares e impares usando dos hilos.

El objetivo es imprimir los números en orden, mientras que un hilo solo imprime los números pares y el otro hilo solo imprime los números impares. Usaremos los conceptos de sincronización de subprocesos y comunicación entre subprocesos para resolver el problema.

2. Hilos en Java

Los subprocesos son procesos ligeros que se pueden ejecutar al mismo tiempo. La ejecución simultánea de varios subprocesos puede ser buena en cuanto al rendimiento y la utilización de la CPU, ya que podemos trabajar en más de una tarea a la vez a través de diferentes subprocesos que se ejecutan en paralelo.

Puede encontrar más información sobre los hilos en Java en este artículo.

En Java, podemos crear un hilo extendiendo la clase Thread o implementando la interfaz Runnable . En ambos casos, anulamos el método de ejecución y escribimos la implementación del hilo en él.

Puede encontrar más información sobre cómo utilizar estos métodos para crear un hilo aquí.

3. Sincronización de subprocesos

En un entorno de subprocesos múltiples, es posible que dos o más subprocesos accedan al mismo recurso aproximadamente al mismo tiempo. Esto puede ser fatal y producir resultados erróneos. Para evitar esto, debemos asegurarnos de que solo un subproceso acceda al recurso en un momento determinado.

Podemos lograr esto usando la sincronización de subprocesos.

En Java, podemos marcar un método o bloque como sincronizado, lo que significa que solo un hilo podrá ingresar a ese método o bloque en un momento dado.

Puede encontrar más detalles sobre la sincronización de subprocesos en Java aquí.

4. Comunicación entre hilos

La comunicación entre subprocesos permite que los subprocesos sincronizados se comuniquen entre sí mediante un conjunto de métodos.

Los métodos utilizados son esperar , notificar y notificar a todos, todos heredados de la clase Object .

Wait () hace que el hilo actual espere indefinidamente hasta que otras llamadas de hilo notifiquen () o notifiquen a todos () en el mismo objeto. Podemos llamar a notify () para despertar los hilos que están esperando acceso al monitor de este objeto.

Puede encontrar más detalles sobre el funcionamiento de estos métodos aquí.

5. Impresión alternativa de números pares e impares

5.1. Usando esperar () y notificar ()

Usaremos los conceptos discutidos de sincronización y comunicación entre hilos para imprimir números pares e impares en orden ascendente utilizando dos hilos diferentes.

En el primer paso, implementaremos la interfaz Runnable para definir la lógica de ambos hilos . En el método de ejecución , verificamos si el número es par o impar.

Si el número es par, llamamos al método printEven de la clase Printer , de lo contrario llamamos al método printOdd :

class TaskEvenOdd implements Runnable { private int max; private Printer print; private boolean isEvenNumber; // standard constructors @Override public void run() { int number = isEvenNumber ? 2 : 1; while (number <= max) { if (isEvenNumber) { print.printEven(number); } else { print.printOdd(number); } number += 2; } } } 

Definimos la clase Printer de la siguiente manera:

class Printer { private volatile boolean isOdd; synchronized void printEven(int number) { while (!isOdd) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } System.out.println(Thread.currentThread().getName() + ":" + number); isOdd = false; notify(); } synchronized void printOdd(int number) { while (isOdd) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } System.out.println(Thread.currentThread().getName() + ":" + number); isOdd = true; notify(); } }

En el método principal, usamos la clase definida para crear dos hilos. Creamos un objeto de la clase Printer y lo pasamos como parámetro al constructor TaskEvenOdd :

public static void main(String... args) { Printer print = new Printer(); Thread t1 = new Thread(new TaskEvenOdd(print, 10, false),"Odd"); Thread t2 = new Thread(new TaskEvenOdd(print, 10, true),"Even"); t1.start(); t2.start(); }

El primer hilo será el hilo impar, por lo tanto pasamos falso como valor del parámetro esEvenNumber . Para el segundo hilo, pasamos verdadero en su lugar. Establecemos maxValue en 10 para ambos hilos, de modo que solo se impriman los números del 1 al 10.

Luego, iniciamos ambos hilos llamando al método start () . Esto invocará el método run () de ambos hilos como se definió anteriormente en el que verificamos si el número es par o impar y los imprimimos.

Cuando el hilo impar comienza a ejecutarse, el valor del número de variable será 1. Dado que es menor que maxValue y la bandera esEvenNumber es falsa, se llama a printOdd () . En el método, verificamos si la bandera isOdd es verdadera y mientras es verdadera llamamos a wait (). Dado que isOdd es falso inicialmente, no se llama a wait () y se imprime el valor.

Luego, establecemos el valor de isOdd en verdadero, de modo que el hilo impar entre en estado de espera y llame a notify () para despertar el hilo par. Luego, el hilo par se activa e imprime el número par, ya que la bandera impar es falsa. Luego llama a notificar () para despertar el hilo impar.

El mismo proceso se lleva a cabo hasta que el valor del número de variable sea ​​mayor que maxValue .

5.2. Usando semáforos

Un semáforo controla el acceso a un recurso compartido mediante el uso de un contador. Si el contador es mayor que cero, se permite el acceso . Si es cero, se deniega el acceso.

Java proporciona la clase Semaphore en el paquete java.util.concurrent y podemos usarla para implementar el mecanismo explicado. Puede encontrar más detalles sobre los semáforos aquí.

Creamos dos hilos, un hilo impar y un hilo par. El hilo impar imprimirá los números impares a partir de 1, y el hilo par imprimirá los números pares a partir de 2.

Ambos subprocesos tienen un objeto de la clase SharedPrinter . La clase SharedPrinter tendrá dos semáforos, semOdd y semEven, que tendrán permisos 1 y 0 para comenzar . Esto asegurará que el número impar se imprima primero.

We have two methods printEvenNum() and printOddNum(). The odd thread calls the printOddNum() method and the even thread calls the printEvenNum() method.

To print an odd number, the acquire() method is called on semOdd, and since the initial permit is 1, it acquires the access successfully, prints the odd number and calls release() on semEven.

Calling release() will increment the permit by 1 for semEven, and the even thread can then successfully acquire the access and print the even number.

This is the code for the workflow described above:

public static void main(String[] args) { SharedPrinter sp = new SharedPrinter(); Thread odd = new Thread(new Odd(sp, 10),"Odd"); Thread even = new Thread(new Even(sp, 10),"Even"); odd.start(); even.start(); }
class SharedPrinter { private Semaphore semEven = new Semaphore(0); private Semaphore semOdd = new Semaphore(1); void printEvenNum(int num) { try { semEven.acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(Thread.currentThread().getName() + num); semOdd.release(); } void printOddNum(int num) { try { semOdd.acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(Thread.currentThread().getName() + num); semEven.release(); } } class Even implements Runnable { private SharedPrinter sp; private int max; // standard constructor @Override public void run() { for (int i = 2; i <= max; i = i + 2) { sp.printEvenNum(i); } } } class Odd implements Runnable { private SharedPrinter sp; private int max; // standard constructors @Override public void run() { for (int i = 1; i <= max; i = i + 2) { sp.printOddNum(i); } } }

6. Conclusion

En este tutorial, vimos cómo podemos imprimir números pares e impares alternativamente usando dos hilos en Java. Echamos un vistazo a dos métodos para lograr los mismos resultados: usar wait () y notificar () y usar un semáforo .

Y, como siempre, el código de trabajo completo está disponible en GitHub.