El mecanismo de sincronización de subprocesos se puede lograr utilizando el marco de bloqueo, que está presente en el paquete java.util.concurrent . El marco de bloqueo funciona como bloques sincronizados, excepto que los bloqueos pueden ser más sofisticados que los bloques sincronizados de Java. Los bloqueos permiten una estructuración más flexible del código sincronizado. Este nuevo enfoque se introdujo en Java 5 para abordar el problema de sincronización mencionado a continuación.
Veamos una clase Vector, que tiene muchos métodos sincronizados. Cuando hay 100 métodos sincronizados en una clase, solo se puede ejecutar un subproceso de estos 100 métodos en un momento dado. Solo se permite que un subproceso acceda a un solo método en un momento determinado mediante un bloque sincronizado. Esta es una operación muy costosa. Los bloqueos evitan esto al permitir la configuración de varios bloqueos para diferentes propósitos. Uno puede tener un par de métodos sincronizados bajo un bloqueo y otros métodos bajo un bloqueo diferente. Esto permite una mayor concurrencia y también aumenta el rendimiento general.
Ejemplo:
Lock lock = new ReentrantLock(); lock.lock(); // Critical section lock.unlock();
Un bloqueo se adquiere mediante el método lock() y se libera mediante el método unlock(). Invocar un unlock() sin lock() generará una excepción. Como ya se mencionó, la interfaz de bloqueo está presente en el paquete java.util.concurrent.locks y ReentrantLock implementa la interfaz de bloqueo.
Nota: El número de llamadas lock() siempre debe ser igual al número de llamadas unlock().
En el siguiente código, el usuario ha creado un recurso llamado «TestResource» que tiene dos métodos y dos bloqueos diferentes para cada uno respectivamente. Hay dos trabajos llamados «DisplayJob» y «ReadJob». La clase LockTest crea 5 subprocesos para realizar ‘DisplayJob’ y 5 subprocesos para realizar ‘ReadJob’. Los 10 subprocesos comparten un solo recurso «TestResource».
Java
import java.util.Date; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; // Test class to test the lock example // 5 threads are created with DisplayJob // and 5 thread are created with ReadJob. // Both these jobs use single TestResource named "test". public class LockTest { public static void main(String[] args) { TestResource test = new TestResource(); Thread thread[] = new Thread[10]; for (int i = 0; i < 5; i++) { thread[i] = new Thread(new DisplayJob(test), "Thread " + i); } for (int i = 5; i < 10; i++) { thread[i] = new Thread(new ReadJob(test), "Thread " + i); } for (int i = 0; i < 10; i++) { thread[i].start(); } } } // DisplayJob class implementing Runnable interface. // This uses TestResource object passed in the constructor. // run method invokes displayRecord method on TestResource. class DisplayJob implements Runnable { private TestResource test; DisplayJob(TestResource tr) { test = tr; } @Override public void run() { System.out.println("display job"); test.displayRecord(new Object()); } } // ReadJob class implementing Runnable interface. // which uses TestResource object passed in the constructor. // run method invokes readRecord method on TestResource. class ReadJob implements Runnable { private TestResource test; ReadJob(TestResource tr) { test = tr; } @Override public void run() { System.out.println("read job"); test.readRecord(new Object()); } } // Class which has two locks and two methods. class TestResource { // displayQueueLock is created to make // displayQueueLock thread safe. // When T1 acquires lock on testresource(o1) // object displayRecord method // T2 has to wait for lock to be released // by T1 on testresource(o1) object // displayRecord method. But T3, can execute // readRecord method with out waiting for lock // to be released by t1 as // readRecord method uses readQueueLock not // displayQueueLock. private final Lock displayQueueLock = new ReentrantLock(); private final Lock readQueueLock = new ReentrantLock(); // displayRecord uses displayQueueLock to // achieve thread safety. public void displayRecord(Object document) { final Lock displayLock = this.displayQueueLock; displayLock.lock(); try { Long duration = (long) (Math.random() * 10000); System.out.println(Thread.currentThread(). getName() + ": TestResource: display a Job"+ " during " + (duration / 1000) + " seconds ::"+ " Time - " + new Date()); Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.printf("%s: The document has been"+ " dispalyed\n", Thread.currentThread().getName()); displayLock.unlock(); } } // readRecord uses readQueueLock to achieve thread safety. public void readRecord(Object document) { final Lock readQueueLock = this.readQueueLock; readQueueLock.lock(); try { Long duration = (long) (Math.random() * 10000); System.out.println (Thread.currentThread().getName() + ": TestResource: reading a Job during " + (duration / 1000) + " seconds :: Time - " + new Date()); Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.printf("%s: The document has"+ " been read\n", Thread.currentThread().getName()); readQueueLock.unlock(); } } }
Salida :
trabajo
de visualización trabajo
de visualización trabajo de visualización trabajo de
visualización trabajo de
visualización trabajo de
lectura trabajo de
lectura trabajo de
lectura trabajo de
lectura trabajo de
lectura
Subproceso 5: TestResource: lectura de un trabajo durante 4 segundos::Hora: miércoles 27 de febrero a las 15:49:53 UTC 2019
Subproceso 0: TestResource: mostrar un Trabajo durante 6 segundos::Hora – Mié 27 de febrero 15:49:53 UTC 2019
Subproceso 5: El documento ha sido leído
Subproceso 6: TestResource: leyendo un Trabajo durante 4 segundos::Hora – Mié 27 de febrero 15:49: 58 UTC 2019
En el ejemplo anterior, DisplayJob no requiere esperar a que los subprocesos ReadJob completen la tarea, ya que ReadJob y Display job utilizan dos bloqueos diferentes. Esto no puede ser posible con «sincronizado».
Las diferencias son las siguientes:
Parámetros | Marco de bloqueo | sincronizado |
---|---|---|
A través de métodos | Sí, los bloqueos se pueden implementar en todos los métodos, puede invocar lock() en el método 1 e invocar unlock() en el método 2. | Imposible |
tratar de adquirir bloqueo | sí, el método de bloqueo (tiempo de espera) es compatible con el marco de bloqueo, que bloqueará el recurso si está disponible; de lo contrario, devuelve falso y Thread no se bloqueará. | No es posible con sincronizado |
Gestión de bloqueo justo | Sí, la gestión de bloqueo justo está disponible en caso de marco de bloqueo. Entrega la cerradura a un largo hilo de espera. Incluso en el modo de equidad establecido en verdadero, si se codifica el bloqueo de prueba, se sirve primero. | No es posible con sincronizado |
Lista de hilos en espera | Sí, la lista de subprocesos en espera se puede ver usando el marco de bloqueo | No es posible con sincronizado |
Liberación de bloqueo en excepciones |
Lock.lock(); myMethod();Lock.unlock(); unlock() no se puede ejecutar en este código si se lanza alguna excepción desde myMethod(). |
Synchronized funciona claramente en este caso. Libera la cerradura |
Publicación traducida automáticamente
Artículo escrito por haripriyapendem y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA