Java: sincronización de subprocesos

Cuando iniciamos dos o más subprocesos dentro de un programa, puede haber una situación en la que varios subprocesos intenten acceder al mismo recurso y, finalmente, pueden producir resultados imprevistos debido a problemas de concurrencia. Por ejemplo, si varios subprocesos intentan escribir dentro de un mismo archivo, es posible que corrompan los datos porque uno de los subprocesos puede anular los datos o, mientras un subproceso abre el mismo archivo al mismo tiempo, otro subproceso podría estar cerrando el mismo archivo.

Por lo tanto, es necesario sincronizar la acción de varios subprocesos y asegurarse de que solo un subproceso pueda acceder al recurso en un momento determinado. Esto se implementa utilizando un concepto llamadomonitors. Cada objeto en Java está asociado con un monitor, que un hilo puede bloquear o desbloquear. Solo un hilo a la vez puede mantener un bloqueo en un monitor.

El lenguaje de programación Java proporciona una forma muy práctica de crear subprocesos y sincronizar su tarea utilizando synchronizedbloques. Mantienes los recursos compartidos dentro de este bloque. A continuación se muestra la forma general de la declaración sincronizada:

Sintaxis

synchronized(objectidentifier) {
   // Access shared variables and other shared resources
}

Aquí el objectidentifieres una referencia a un objeto cuyo bloqueo se asocia con el monitor que representa la instrucción sincronizada. Ahora vamos a ver dos ejemplos, donde imprimiremos un contador usando dos hilos diferentes. Cuando los subprocesos no están sincronizados, imprimen el valor del contador que no está en secuencia, pero cuando imprimimos el contador colocando dentro del bloque sincronizado (), imprime el contador muy en secuencia para ambos subprocesos.

Ejemplo de subprocesos múltiples sin sincronización

Aquí hay un ejemplo simple que puede o no imprimir el valor del contador en secuencia y cada vez que lo ejecutamos, produce un resultado diferente basado en la disponibilidad de la CPU para un hilo.

Ejemplo

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      PD.printCount();
      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[]) {

      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
         try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Esto produce un resultado diferente cada vez que ejecuta este programa:

Salida

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   5
Counter   ---   2
Counter   ---   1
Counter   ---   4
Thread Thread - 1  exiting.
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Ejemplo de subprocesos múltiples con sincronización

Aquí está el mismo ejemplo que imprime el valor del contador en secuencia y cada vez que lo ejecutamos, produce el mismo resultado.

Ejemplo

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      synchronized(PD) {
         PD.printCount();
      }
      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[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Esto produce el mismo resultado cada vez que ejecuta este programa:

Salida

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.