Nuestros sistemas funcionan en un entorno de subprocesos múltiples que se convierte en una parte importante del sistema operativo para proporcionar una mejor utilización de los recursos. El proceso de ejecutar dos o más partes del programa simultáneamente se conoce como Multithreading. Un programa es un conjunto de instrucciones en las que se ejecutan múltiples procesos y, dentro de un proceso, funcionan múltiples subprocesos. Los hilos no son más que procesos ligeros. Por ejemplo, en la computadora estamos jugando videojuegos al mismo tiempo que estamos trabajando con MS Word y escuchando música. Entonces, estos son los procesos en los que estamos trabajando simultáneamente. En esto, cada aplicación tiene múltiples subprocesos, es decir, subprocesos. En el ejemplo anterior, escuchamos música en la que tenemos un reproductor de música como una aplicación que contiene múltiples subprocesos que se ejecutan como administrar la lista de reproducción,
Esta es la introducción básica de subprocesos múltiples que ayudará aún más a comprender la importancia de la sincronización de subprocesos.
Prioridades de subprocesos
En java, a cada subproceso se le asigna una prioridad que determina cómo se deben tratar los subprocesos entre sí. La prioridad del subproceso se utiliza para decidir cuándo cambiar de un subproceso en ejecución al siguiente. Un subproceso de mayor prioridad puede adelantarse a un subproceso de menor prioridad y puede requerir más tiempo de CPU. De manera simple, un hilo con mayor prioridad obtiene el recurso primero en comparación con el hilo con menor prioridad. Pero, en caso de que dos subprocesos con la misma prioridad quieran el mismo recurso, la situación se vuelve más complicada. Entonces, en un entorno de subprocesos múltiples, si los subprocesos con la misma prioridad están trabajando con el mismo recurso, se obtienen resultados no deseados o código erróneo.
Tomemos un ejemplo. En una habitación, tenemos varias computadoras conectadas a una sola impresora. En un momento, una computadora quiere imprimir un documento, por lo que usa una impresora. Al mismo tiempo, otra computadora quiere que la impresora imprima su documento. Entonces, dos computadoras exigen el mismo recurso, es decir, una impresora. Entonces, si ambos procesos se ejecutan juntos, la impresora imprimirá el documento de una y otra computadora. Esto producirá una salida no válida. Ahora, lo mismo sucede en el caso de los subprocesos si dos subprocesos con la misma prioridad o que desean el mismo recurso generan resultados inconsistentes.
En Java, cuando dos o más subprocesos intentan acceder al mismo recurso simultáneamente, el tiempo de ejecución de Java ejecuta uno o más subprocesos lentamente, o incluso suspende su ejecución. Para superar este problema, tenemos sincronización de subprocesos.
Sincronización significa coordinación entre múltiples procesos/hilos.
Tipos de sincronización:
Existen dos tipos de sincronización que son los siguientes:
- Sincronización de procesos
- Sincronización de subprocesos
Aquí nos centraremos principalmente en la sincronización de subprocesos.
La sincronización de subprocesos se refiere básicamente al concepto de ejecución de un subproceso a la vez y el resto de los subprocesos están en estado de espera . Este proceso se conoce como sincronización de subprocesos. Previene la interferencia del hilo y el problema de inconsistencia.
La sincronización se construye usando bloqueos o monitores . En Java, un monitor es un objeto que se usa como un candado mutuamente excluyente. Solo un único subproceso a la vez tiene derecho a poseer un monitor. Cuando un subproceso obtiene un bloqueo, todos los demás subprocesos se suspenderán y están tratando de adquirir el monitor bloqueado. Entonces, se dice que otros subprocesos están esperando al monitor, hasta que el primer subproceso sale del monitor. De una manera simple, cuando un subproceso solicita un recurso, ese recurso se bloquea para que ningún otro subproceso pueda funcionar o realizar ninguna modificación hasta que se libere el recurso.
La sincronización de subprocesos es de dos tipos:
- Exclusivo mutuo
- Comunicación entre subprocesos
A. Mutua Exclusividad
Al compartir cualquier recurso, esto mantendrá el hilo interfiriendo entre sí, es decir, excluyendo mutuamente. Podemos lograr esto a través de
- Método sincronizado
- Bloque sincronizado
- Sincronización estática
Método sincronizado
Podemos declarar un método como sincronizado usando la palabra clave «sincronizado» . Esto hará que el código escrito dentro del método sea seguro para subprocesos, de modo que ningún otro subproceso se ejecute mientras se comparte el recurso.
Implementación:
Estaremos proponiendo impresiones de los dos subprocesos simultáneamente mostrando el comportamiento asincrónico sin sincronización de subprocesos.
Ejemplo 1:
Java
// Class 1 // Helper class // Extending Thread class public class PrintTest extends Thread { // Non synchronized Code // Method 1 public void printThread(int n) { // This loop will print the currently executed // thread for (int i = 1; i <= 10; i++) { System.out.println("Thread " + n + " is working..."); // Try block to check for exceptions try { // Pause the execution of current thread // for 0.600 seconds using sleep() method Thread.sleep(600); } // Catch block to handle the exceptions catch (Exception ex) { // Overriding existing toString() method and // prints exception if occur System.out.println(ex.toString()); } } // Display message for better readability System.out.println("--------------------------"); try { // Pause the execution of current thread // for 0.1000 millisecond or 1sec using sleep // method Thread.sleep(1000); } catch (Exception ex) { // Printing the exception System.out.println(ex.toString()); } } } // Class 2 // Helper class extending Thread Class public class Thread1 extends Thread { // Declaring variable of type Class1 PrintTest test; // Constructor for class1 Thread1(PrintTest p) { test = p; } // run() method of this class for // entry point for thread1 public void run() { // Calling method 1 as in above class test.printThread(1); } } // Class 3 // Helper class extending Thread Class public class Thread2 extends Thread { // Declaring variable of type Class1 PrintTest test; // Constructor for class2 Thread2(PrintTest p) { test = p; } // run() method of this class for // entry point for thread2 public void run() { test.printThread(2); } } // Class 4 // Main class public class SynchroTest { // Main driver method public static void main(String[] args) { // Creating object of class 1 inside main() method PrintTest p = new PrintTest(); // Passing the same object of class PrintTest to // both threads Thread1 t1 = new Thread1(p); Thread2 t2 = new Thread2(p); // Start executing the threads // using start() method t1.start(); t2.start(); // This will print both the threads simultaneously } }
Producción:
Ahora, al usar el método sincronizado, bloqueará el objeto para el recurso compartido y brindará un resultado consistente.
Ejemplo 2:
Java
// Java Program Illustrating Lock the Object for // the shared resource giving consistent output // Class 1 // Helper class extending Thread class public class PrintTest extends Thread { // synchronized code // synchronized method will lock the object and // releases when thread is terminated or completed its // execution. synchronized public void printThread(int n) { for (int i = 1; i <= 10; i++) { System.out.println("Thread " + n + " is working..."); try { // pause the execution of current thread // for 600 millisecond Thread.sleep(600); } catch (Exception ex) { // overrides toString() method and prints // exception if occur System.out.println(ex.toString()); } } System.out.println("--------------------------"); try { // pause the execution of current thread for // 1000 millisecond Thread.sleep(1000); } catch (Exception ex) { System.out.println(ex.toString()); } } } // creating thread1 extending Thread Class public class Thread1 extends Thread { PrintTest test; Thread1(PrintTest p) { test = p; } public void run() // entry point for thread1 { test.printThread(1); } } // creating thread2 extending Thread Class public class Thread2 extends Thread { PrintTest test; Thread2(PrintTest p) { test = p; } public void run() // entry point for thread2 { test.printThread(2); } } public class SynchroTest { public static void main(String[] args) { PrintTest p = new PrintTest(); // passing the same object of class PrintTest to // both threads Thread1 t1 = new Thread1(p); Thread2 t2 = new Thread2(p); // start function will execute the threads t1.start(); t2.start(); } }
Producción:
B. Bloque sincronizado
Si declaramos un bloque como sincronizado, solo el código que está escrito dentro de ese bloque se ejecuta secuencialmente, no el código completo. Esto se usa cuando queremos acceso secuencial a alguna parte del código o para sincronizar alguna parte del código.
Sintaxis:
synchronized (object reference) { // Insert code here }
Ejemplo
Java
// Java Program Illustrating Synchronized Code // Using synchronized block // Class 1 // Helper class extending Thread class class PrintTest extends Thread { // Method 1 // To print the thread public void printThread(int n) { // Making synchronized block that makes the block // synchronized synchronized (this) { // Iterating using for loop for (int i = 1; i <= 10; i++) { // Print message when these thread are // executing System.out.println("Thread " + n + " is working..."); // Try block to check for exceptions try { // Making thread to pause for 0.6 // seconds Thread.sleep(600); } // Catch block to handle exceptions catch (Exception ex) { // Print message when exception.s occur System.out.println(ex.toString()); } } } // Display message only System.out.println("--------------------------"); try { // Making thread t osleep for 1 sec Thread.sleep(1000); } catch (Exception ex) { System.out.println(ex.toString()); } } } // Class 2 // Helper class extending Thread class class Thread1 extends Thread { PrintTest test; Thread1(PrintTest p) { test = p; } public void run() { test.printThread(1); } } // Class 3 // Helper class extending Thread class class Thread2 extends Thread { PrintTest test; Thread2(PrintTest p) { test = p; } public void run() { test.printThread(2); } } // Class 4 // Main class class SynchroTest { // Main driver method public static void main(String[] args) { // Creating instance for class 1 inside main() PrintTest p = new PrintTest(); // Creating threads and // passing same object Thread1 t1 = new Thread1(p); Thread2 t2 = new Thread2(p); // Starting these thread using start() method t1.start(); t2.start(); } }
Producción:
C. Sincronización estática
En esto, el método sincronizado se declara como «estático» , lo que significa que el bloqueo o el monitor se aplica a la clase, no al objeto, de modo que solo un subproceso accederá a la clase a la vez.
Ejemplo
Java
// Java Program Illustrate Synchronized // Using static synchronization // Class 1 // Helper class class PrintTest extends Thread { // Static synchronization locks the class PrintTest synchronized public static void printThread(int n) { for (int i = 1; i <= 10; i++) { // Print message when threads are executing System.out.println("Thread " + n + " is working..."); // Try block to check for exceptions try { // making thread to sleep for 0.6 seconds Thread.sleep(600); } // Catch block to handle the exceptions catch (Exception ex) { // Print message when exception occurs System.out.println(ex.toString()); } } // Display message for better readability System.out.println("--------------------------"); try { Thread.sleep(1000); } catch (Exception ex) { System.out.println(ex.toString()); } } } // Class 2 // Helper class extending Thread class class Thread1 extends Thread { // run() method for thread public void run() { // Passing the class not the object PrintTest.printThread(1); } } // Class 3 // Helper class extending Thread class class Thread2 extends Thread { public void run() { // Passing the class not the object PrintTest.printThread(2); } } // Class 4 // Main class class SynchroTest { // Main driver method public static void main(String[] args) { // No shared object // Creating objects of class 2 and 3 that // are extending to Thread class Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); // Starting thread with help of start() method t1.start(); t2.start(); } }
Producción:
Publicación traducida automáticamente
Artículo escrito por raismily01 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA