Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre como trabajan dos o mas procesos (threads) cuando son iniciados por un programa y esto nos puede derivar en una situacion que estos intenten acceder al mismo recurso al mismo tiempo y esto puede producir resultados erroneos, un ejemplo puede ser el manejo de un archivo donde un proceso puede estar abriendo para escribirlo y al mismo tiempo podemos tener otro proceso que lo esta cerrando lo cual generara un archivo con informacion corrupta.

Anuncios

Entonces para resolver esto necesitamos «sincronizar» dichos procesos y que cada proceso acceda al recurso en su debido tiempo, para esto se implementa un concepto llamado monitores donde cada objeto en Java es asociado a un monitor y al cual el proceso puede bloquear o desbloquear, un proceso por vez puede mantener el bloqueo en un monitor.

Anuncios

Java nos provee una forma muy practica para crear procesos y poder sincronizarlos por medio de bloques sincronizados, el cual mantiene recursos compartidos dentro de este bloque, su sintaxis es la siguiente:

synchronized(objetoId){
// variables y otros recursos compartidos
}
Anuncios

En este caso utilizamos la palabra synchronized para crear nuestro bloque, el objetoId es una referencia al objeto al cual el bloqueo asocia con el monitor que el bloque sincronizado (synchronized) representa, para entender un poco mejor este concepto veremos dos ejemplos donde primero haremos uno sin sincronizar y luego otro sincronizado, pasemos a nuestro primero ejemplo:

PruebaThread.java

class Imprimir
{
        public void conteo(String nombreThread)
        {
          try
          {
                for(int i = 5; i > 0; i--)
                  System.out.println("Contador de " + nombreThread +
                                                " ---- " + i);
          }
          catch (Exception e)
          {
                System.out.println("Proceso interrumpido.");
          }
        }
}

class ThreadDemo extends Thread
{
        private Thread t;
        private String nombreThread;
        Imprimir I;

        ThreadDemo( String nombre, Imprimir i )
        {
                nombreThread = nombre;
                I = i;
        }

        public void run()
        {
                I.conteo(nombreThread);
                System.out.println("Proceso " + nombreThread + " saliendo.");
        }

        public void start()
        {
                System.out.println("Iniciando " + nombreThread );
                if (t == null)
                {
                        t = new Thread(this, nombreThread);
                        t.start();
                }
        }
}

public class PruebaThread
{
        public static void main(String[] args)
        {
                Imprimir I = new Imprimir();
                ThreadDemo T1 = new ThreadDemo("Proceso - 1", I);
                ThreadDemo T2 = new ThreadDemo("Proceso - 2", I);
                T1.start();
                T2.start();

                try
                {
                        T1.join();
                        T2.join();
                }
                catch(Exception e)
                {
                        System.out.println("Interrumpido");
                }
        }
}
Anuncios

En este ejemplo, primero crearemos una clase la cual se encargara de imprimir un contador el cual se asociara a un proceso el cual se llamara conteo, en este caso le informaremos el nombre de nuestro proceso instanciado, este sera un simple contador de cuenta regresiva de 5 a 1, nuestra siguiente clase sera para extender la clase Thread, en ella crearemos tres variables una para nuestro proceso (t) otra para guardar el nombre que le asignemos (nombreThread) y por ultimo una para la clase Imprimir (I), despues tendremos un constructor para asignarles un elemento tanto a nombreThread como a I por medio de los valores que recibamos en el mismo, despues tendremos la sobreescritura del metodo run() donde por medio de I llamaremos a conteo() de la clase Imprimir, observen como le enviamos el valor asignado a nombreThread, mostrara el conteo y por ultimo mostrara un mensaje cuando salga del proceso e informa cual es el que esta saliendo, despues sobreescribiremos a start() donde mostrara cual es el proceso que iniciamos, por medio de nombreThread, para luego chequear si t es igual a null y en caso de ser verdadero creara un nuevo objeto Thread en t, donde primero informaremos la referencia, en este caso this, y luego el nombre almacenado en nombreThread para luego iniciarlo por medio de start(), metodo propio de la clase Thread y no de ThreadDemo.

Anuncios

Por ultimo tendremos nuestra clase principal donde crearemos un objeto llamado I de la clase Imprimir donde despues crearemos dos objetos de la clase ThreadDemo llamados T1 y T2 donde les asignaremos los nombres Proceso-1 y Proceso-2 respectivamente y en ambos casos pasaremos el objeto llamado I, despues iniciaremos a T1 y T2 para luego por medio del try/catch y de los join esperamos a que terminen los mismos, si lo compilamos y ejecutamos podremos obtener una salida semejante a esta:

tinchicus@dbn001vrt:~/programacion/java/codigo$ java PruebaThread
Iniciando Proceso - 1
Iniciando Proceso - 2
Contador de Proceso - 1 ---- 5
Contador de Proceso - 2 ---- 5
Contador de Proceso - 1 ---- 4
Contador de Proceso - 2 ---- 4
Contador de Proceso - 1 ---- 3
Contador de Proceso - 1 ---- 2
Contador de Proceso - 1 ---- 1
Proceso Proceso - 1 saliendo.
Contador de Proceso - 2 ---- 3
Contador de Proceso - 2 ---- 2
Contador de Proceso - 2 ---- 1
Proceso Proceso - 2 saliendo.
tinchicus@dbn001vrt:~/programacion/java/codigo$
Nota: La salida puede variar e inclusive puede salir correctamente ordenada dependera mucho de sus equipos.
Anuncios

En este caso despues de muchos intentos, muchos intentos, obtuve una salida donde se puede ver como pueden intercalarse algunos procesos entre otros que para determinadas tareas puede resultar incomodo e inapropiado para solucionar esto tomaremos el ejemplo anterior y modificaremos el metodo run() actual:

public void run()
{
        I.conteo(nombreThread);
        System.out.println("Proceso " + nombreThread + " saliendo.");
}

Por este otro:

public void run()
{
        synchronized(I)
        {
                I.conteo(nombreThread);
        }
        System.out.println("Proceso " + nombreThread + " saliendo.");
}
Anuncios

Observen como en este caso utilizamos a synchronized para el metodo de conteo solamente sin necesidad de incluir el mensaje de salida, si lo compilamos y ejecutamos obtendemos siempre esta salida:

tinchicus@dbn001vrt:~/programacion/java/codigo$ java PruebaThread
Iniciando Proceso - 1
Iniciando Proceso - 2
Contador de Proceso - 1 ---- 5
Contador de Proceso - 1 ---- 4
Contador de Proceso - 1 ---- 3
Contador de Proceso - 1 ---- 2
Contador de Proceso - 1 ---- 1
Proceso Proceso - 1 saliendo.
Contador de Proceso - 2 ---- 5
Contador de Proceso - 2 ---- 4
Contador de Proceso - 2 ---- 3
Contador de Proceso - 2 ---- 2
Contador de Proceso - 2 ---- 1
Proceso Proceso - 2 saliendo.
tinchicus@dbn001vrt:~/programacion/java/codigo$
Anuncios

En este caso obtendremos un resultado ideal para nuestros procesos donde podemos ver como proceso se corrio de forma correcta y por separado, y todo esto gracias a una simple modificacion donde hacemos la sincronizacion por medio de synchronized, nuestro codigo final es el siguiente:

PruebaThread.java

class Imprimir
{
        public void conteo(String nombreThread)
        {
          try
          {
                for(int i = 5; i > 0; i--)
                  System.out.println("Contador de " + nombreThread +
                                                " ---- " + i);
          }
          catch (Exception e)
          {
                System.out.println("Proceso interrumpido.");
          }
        }
}

class ThreadDemo extends Thread
{
        private Thread t;
        private String nombreThread;
        Imprimir I;

        ThreadDemo( String nombre, Imprimir i )
        {
                nombreThread = nombre;
                I = i;
        }

        public void run()
        {
                synchronized(I)
                {
                        I.conteo(nombreThread);
                }
                System.out.println("Proceso " + nombreThread + " saliendo.");
        }

        public void start()
        {
                System.out.println("Iniciando " + nombreThread );
                if (t == null)
                {
                        t = new Thread(this, nombreThread);
                        t.start();
                }
        }
}

public class PruebaThread
{
        public static void main(String[] args)
        {
                Imprimir I = new Imprimir();
                ThreadDemo T1 = new ThreadDemo("Proceso - 1", I);
                ThreadDemo T2 = new ThreadDemo("Proceso - 2", I);
                T1.start();
                T2.start();

                try
                {
                        T1.join();
                        T2.join();
                }
                catch(Exception e)
                {
                        System.out.println("Interrumpido");
                }
        }
}
Anuncios

En resumen, hoy hemos visto como se puede sincronizar distintos procesos para que no se superpongan entre ellos, si bien hemos utilizado un ejemplo muy basico en otros casos puede ser util y necesario si se necesita un correcto procesamiento de nuestro programa, hemos visto la utilidad de synchronized, un ejemplo sin esta ayuda, otro con esta ayuda, hemos visto su sintaxis basica y hablado de algunas probables fallas, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.50

Anuncio publicitario