Programación concurrente. Procesos en Java

Ejemplo de programación concurrente en Java e introducción de cómo controlar procesos con monitores

En este artículo veremos cómo interactua un programa con dos procesos (threads) independientes que se van intercalando según el procesador los va ejecutando. También introduciremos el concepto de monitor para controlar procesos comunes.

import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {
    public synchronized static void main(String[] args) {
        Mensaje proceso1 = new Mensaje("hola", 100);
        proceso1.start();
        Mensaje proceso2 = new Mensaje("adiós", 100);
        proceso2.start();
    }
}
class Mensaje extends Thread {

    private String texto;
    private int tiempo;

    public Mensaje(String texto, int tiempo) {
        this.texto = texto;
        this.tiempo = tiempo;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                System.out.print(texto + " ");
                sleep(tiempo);
            } catch (InterruptedException ex) {
                Logger.getLogger(PingPong.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

Nos aparecerá por pantalla una cadena de texto del tipo: hola adiós hola adiós hola adiós hola adiós hola adiós hola adiós hola adiós adiós hola hola adiós hola adiós, intercalándose cada vez que se ejecute de forma distinta según el procesador vaya ejecutando los hilos. Podéis cambiar el tiempo entre que se lanza cada proceso para observar distintas ejecuciones.

Monitores

En el caso anterior los dos procesos eran independientes, pero qué pasa si por ejemplo quisieramos que no se escribiera "adiós" si antes no hubiera un "hola" y viceversa? En ese caso habría que crear un Monitor.

Un monitor es una clase externa que sirve para controlar procesos que tienen elementos en común y en cierta forma tener un cierto control sobre qué proceso se puede o no ejecutar.

Para implementar el ejemplo propuesto veamos este ejemplo:

import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {

    public static void main(String args[]) {

        Monitor monitor = new Monitor();
        Escribir hola = new Escribir("hola", 0, monitor);
        hola.start();
        Escribir adios = new Escribir("adiós", 1, monitor);
        adios.start();
    }
}
class Escribir extends Thread {

    private Monitor monitor;
    private String texto;
    private int orden;

    public Escribir(String texto, int orden, Monitor monitor) {
        this.texto = texto;
        this.monitor = monitor;
        this.orden = orden;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                monitor.escribir(texto + " ", orden);
                sleep((int) (Math.random() * 10));
            } catch (InterruptedException ex) {
                Logger.getLogger(PingPong.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
class Monitor {

    private int actual = 0;

    public synchronized void escribir(String texto, int orden) {

        while (orden != actual) {
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(ProcesEscriure.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        System.out.print(texto);
        actual = (actual + 1) % 2;
        notifyAll();
    }
}

En este caso creamos la clase Monitor que contiene un entero que indica qué proceso se ejecutará. Como los dos procesos manejan datos en común, en este caso Monitor, es imprescindible usar la palabra clave synchronized para bloquear las posiciones de memoria que usa el método escribir. Luego dentro del while, pausaremos todos los procesos que no les toque ejecutarse. Y ya fuera del while cuando le toque ejecutarse el proceso correspondiente usaremos el método notifyAll() para despertar a todos los threads que estaban em modo espera.

De esta forma conseguimos un intercalamiento de palabras.