Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre la llamada comunicacion interthread o comunicacion entre procesos la cual es utilizada cuando tenemos un programa que tiene dos o mas procesos y entre ellos se intercambian informacion, veamos tres metodos disponibles que nos facilitan la comunicacion entre procesos:

  • public void wait(), ocasiona que el proceso actual espere hasta que otro proceso invoque al notify()
  • public void notify(), reanuda a un solo proceso que esta esperando al monitor del objeto
  • public void notifyAll(), reanuda todos los procesos que llamaron a wait() en el mismo objeto
Anuncios

Estos metodos son implementados como tipo final en un objeto, por lo tanto estan disponibles en todo tipo de clases y estos metodos solo pueden ser llamados dentro del contexto de un bloque sincronizado (synchronized), para entenderlo un poco mejor veamos el siguiente ejemplo donde implementaremos a wait() y notify():

ProcesoPrueba.java

class Chat
{
        boolean flag = false;

        public synchronized void Pregunta(String msn)
        {
                if (flag)
                {
                        try
                        {
                          wait();
                        }
                        catch (InterruptedException e)
                        {
                                e.printStackTrace();
                        }
                }
                System.out.println(msn);
                flag = true;
                notify();
        }

        public synchronized void Respuesta(String msn)
        {
                if (!flag)
                {
                        try
                        {
                          wait();
                        }
                        catch (InterruptedException e)
                        {
                                e.printStackTrace();
                        }
                }
                System.out.println(msn);
                flag = false;
                notify();
        }
}

class T1 implements Runnable
{
        Chat m;
        String[] s1 = { "Holis","Como estas?","Yo tambien estoy bien!" };

        public T1(Chat m1)
        {
                this.m = m1;
                new Thread(this, "Pregunta").start();
        }

        public void run()
        {
                for(int i = 0; i < s1.length; i++)
                        m.Pregunta(s1[i]);
        }
}

class T2 implements Runnable
{
        Chat m;
        String[] s2 = { "Hola","Estoy bien, y tu como estas?","Buenisimo!" };

        public T2(Chat m2)
        {
                this.m = m2;
                new Thread(this, "Respuesta").start();
        }

        public void run()
        {
                for(int i = 0; i < s2.length; i++)
                        m.Respuesta(s2[i]);
        }
}

public class ProcesoPrueba
{
        public static void main(String[] args)
        {
                Chat m = new Chat();
                new T1(m);
                new T2(m);
        }
}
Anuncios
Anuncios

Para este ejemplo veremos nuestra primera clase llamada Chat, primero crearemos una variable llamada flag de tipo boolean y le asignaremos un valor false, en esta clase luego tendremos un metodo llamado Pregunta de tipo synchronized, el cual tendra un bloque try/catch donde llamara a wait() y en caso de error mostrara un mensaje del stack, en este caso sino ocurre nada mostrara el mensaje informado, pasara la variable flag a true y luego llamara a notify(), nuestro siguiente metodo llamado Respuesta tambien de tipo synchronized hace exactamente lo mismo pero la diferencia esta en el bloque if donde en lugar de esperar un flag de tipo true esperara un flag de valor false, esto por medio del negador (!) y seteamos la variable flag en estado false el respuesta hace exactamente lo mismo.

Anuncios
Anuncios

Nuestra siguiente clase se llama T1 donde implementaremos a la clase Runnable, primero crearemos una variable de tipo Chat llamada m, luego un array de tipo String donde ingresaremos tres textos y llamaremos s1, despues implementaremos un constructor donde recibira un atributo de tipo Chat, este atributo sera asignado a la variable m de nuestra clase y despues crearemos un nuevo Thread (proceso) llamado Pregunta y lo iniciaremos, nuestro siguiente metodo se llama run() donde tendra un bucle for que tomara como limite el tamaño del array creado llamado s1, y por medio del objeto m instanciaremos a Pregunta y le enviaremos el texto correspondiente al ordinal de i.

Anuncios

La clase siguiente es T2 que es exactamente igual a la clase anterior pero las unicas diferencias son en el constructor donde llamamos al Thread como Respuesta y en el metodo run() donde llamaremos al metodo Respuesta en lugar de Pregunta del bucle for, en el cuerpo del main simplemente crearemos un objeto de tipo Chat llamado m y luego crearemos dos nuevas llamadas a la clase T1 y T2 donde le enviaremos a m, si lo compilamos y probamos nuestro programa obtendremos esta salida:

tinchicus@dbn001vrt:~/programacion/java/codigo$ java ProcesoPrueba
Holis
Hola
Como estas?
Estoy bien, y tu como estas?
Yo tambien estoy bien!
Buenisimo!
tinchicus@dbn001vrt:~/programacion/java/codigo$
Anuncios

Como pueden ver gracias a synchronized podemos turnar nuestras preguntas y respuestas, observen como primero pregunta T1 y luego responde T2 y luego se ve como se hace correctamente.

Anuncios

* Actualizacion *

Estuve realizando videos para mi canal de YouTube, donde hago resumenes de muchos de los temas que vemos en este blog. Y cuando regrese para resumir y subir el código que tenemos en este ejemplo, me dí cuenta que podía mejorarlo. Y como lo logré, les voy a pasar el nuevo código y comentarlo. En esta ocasión, decidí trabajar con tres archivos. Siendo el primero para el Chat entre los operadores, el segundo para los operadores y el último para el programa en si. Analicemos el primer código:

Chat.java
class Chat {
  boolean flag = false;
  public synchronized void Dialogo(String msn, boolean std, String sjt) {
     if (flag == std) {
        try {
           wait();
        }
        catch(InterruptedException e) {
           e.printStackTrace();
        }
     }
     System.out.println(sjt + ": " + msn);
     flag = std;
     notify();
  }
}
Anuncios

Primero establecemos a la variable que usaremos para alternar los distintos mensajes en el diálogo entre los operadores. Lo siguiente es el método encargado de crear el diálogo, usamos a synchronized para una mejor manipulación del acceso a los threads. Este método recibe tres valores:

  • msn: para el mensaje
  • std: para el identificador en el diálogo
  • sjt: es para el nombre del operador
Anuncios
Anuncios

Tenemos un condicional donde verifica si la primer variable es igual al valor recibido en el método. En caso de ser verdadero, usa un try/catch para poder manejarr a la excepción InterruptedException. En el bloque del try hacemos un llamado a wait para que pause al thread y espere por su notify. Por fuera de esto, mostramos el nombre del operador y el mensaje que recibimos. Y aqui viene la magia,, porque al establecer el valor recibido en flag lo deja para que el próximo espere por su notify. Lo siguiente es llamar a notify pero en el primer mensaje no tendrá ninguno efecto. En cambio, a partir de este es la base fundamental para realizar el intercambio.

Anuncios

Si lo analizan en el primer código, los dos operadores hacían tareas redundantes porque hacen exactamente lo mismo pero solo cambian algunos valores. Con esto, ya tenemos al código para manejar el chat entre dos operadores. Pasemos a la clase para crearlos.

Operador.java
class Operador implements Runnable {
  Chat m;
  String[] s;
  String n;
  boolean f;
  Operador(Chat c, boolean e, String i, String[] t) {
     this.m = c;
     this.s = t;
     this.f = e;
     this.n = i;
     new Thread(this, "Dialogo").start();
  }
  public void run() {
     for(int i=0; i < s.length; i++)
        m.Dialogo(s[i], f, n);
  }
}
Anuncios
Anuncios

Esta clase implementa a la interface Runnable para poder acceder a los threads. Primero declaramos cuatro variables/objetos que nos serán útiles, no solo para el chat sino para manipular la información en este. El primero es el objeto para contener el chat en si, el segundo es un array donde estarán los distintos mensajes, la tercera es para el nombre del operador y la última es para que dialogo sepa cuando es un operador u otro. En el constructor recibiremos cuatro valores y los usamos para iniciar a los anteriores. Pero antes de salir del bloque iniciamos un thread del objeto actual. Lo siguiente es definir al método run, que para nuestro caso es mas que suficiente, y en este tenemos un bucle que pasará por todos los mensajes. A medida que pasamos, lo enviamos al método Dialogo de Chat, junto al booleano que identifica a cada operador y el nombre de cada uno. Solo nos resta el código para el programa.

Prueba.java
public class Prueba {
  public static void main(String[] args) {
     Chat m = new Chat();
     String[] s1 = {"Hola","Como estas?","yo tambien"};
     String[] s2 = {"Holis","Estoy bien y vos?","Buenisimo"};

     new Operador(m, true, "Operador 1", s1);
     new Operador(m, false, "Operador 2", s2);
  }
}
Anuncios
Anuncios

No es necesario importar nada, simplemente llamar a las clases anteriores pero con una sola condición, que todas esten en el mismo directorio. Primero definimos un objeto de tipo Chat, luego definimos dos arrays con distintos mensajes. Estos representarán los mensajes de cada operador y son los que alternaremos. Y finalmente creamos dos operadores, como trabajan sobre dos threads distintos no es necesario asignarlos, y a ellos pasamos el mismo objeto de chat pero con distintos booleanos. Con este valor, Dialogo se encarga de diferenciarlos y esperar por el otro. El sigueinte dato es el identificador para el chat y finalmente los mensajes de cada uno. Compilemos y veamos como es la salida:

$ java Prueba
Operador 1: Hola
Operador 2: Holis
Operador 1: Como estas?
Operador 2: Estoy bien y vos?
Operador 1: yo tambien
Operador 2: Buenisimo
$
Anuncios

Como pueden ver, seguimos teniendo una salida ordenada para cada mensaje. Pero ahora tenemos una serie de ventajas con respecto al código original. Podemos tener mas de dos operadores, si cambian al identificador booleano por otro, p.e. un int, podrán hacer que todos interactuen sin límites. También pueden agregar más mensajes, ya sea modificando los arrays o pasandolos de otra manera. Asi como también ahora se puede saber de quién es el mensaje. Por esta razón, quise compartir este nuevo código. Ya que me pareció más practico y respeta la doctrina informática de códigos simples y sin redundancias.

Anuncios

En resumen, hoy hemos visto como son las comunicaciones entre procesos, los metodos que podemos usar dentro del bloque sincronizado (synchronized), vimos como implementarlo en un ejemplo, y ahora también agregue una versión mejorada de ese ejemplo. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos agregados hoy:

GitHub / Control de Interthreads

Les dejo algunas de mis redes sociales para seguirme o recibir una notificacion cada vez que subo un nuevo post:

Anuncios

Donación

Es para mantenimento del sitio, gracias!

$1.50