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.