0. Acerca de Mutex
El llamado bloqueo mutex se refiere a una cerradura que solo puede tener un hilo a la vez. Antes de JDK1.5, generalmente usamos el mecanismo sincronizado para controlar el acceso de múltiples hilos a recursos compartidos. Ahora, Lock proporciona una gama más amplia de operaciones de bloqueo que el mecanismo sincronizado. Las principales diferencias entre el bloqueo y los mecanismos sincronizados:
El mecanismo sincronizado proporciona acceso a bloqueos de monitor implícitos asociados con cada objeto, y obliga a todas las adquisiciones y liberación de bloqueos a aparecer en una estructura de bloque. Cuando se adquieren múltiples bloqueos, deben liberarse en orden inverso. El mecanismo sincronizado libera las cerraduras implícitamente. Mientras el código se ejecute por el hilo exceda el alcance del bloque de instrucciones sincronizado, se lanzará el bloqueo. El mecanismo de bloqueo debe llamar explícitamente el método desbloqueo () del objeto de bloqueo para liberar el bloqueo, que proporciona la posibilidad de que la adquisición y la liberación de los bloqueos no aparezcan en la misma estructura de bloque, y libere los bloqueos en un orden más gratuito.
1. Introducción al Reentrantlock
Reentrantlock es un bloqueo mutex reentrante, también conocido como un "bloqueo exclusivo".
Como su nombre lo indica, un bloqueo de reentrantlock solo puede ser sostenido por un bloqueo de hilo en el mismo punto de tiempo; mientras que Reentrant significa que un solo hilo puede adquirir un bloqueo de reentrantlock varias veces.
Reentrantlock se divide en "bloqueo justo" y "bloqueo injusto". Sus diferencias se reflejan en si el mecanismo de obtener cerraduras es justo. "Lock" es proteger los recursos competitivos y evitar que múltiples hilos operen hilos al mismo tiempo y errores. Reentrantlock solo se puede adquirir mediante un hilo al mismo tiempo (cuando un hilo adquiere el "bloqueo", otros hilos deben esperar); Reentraantlock administra todos los hilos que adquieren la cerradura a través de una cola de espera FIFO. Bajo el mecanismo de "bloqueo justo", los hilos hacen cola para adquirir la secuencia de bloqueo; Mientras que el "bloqueo no fair" adquirirá el bloqueo independientemente de si es al comienzo de la cola o no.
Lista de funciones de reentrantlock
// Crear un reentrantlock, que es "bloqueo injusto" de forma predeterminada. Reentrantlock () // La política de creación es el Reentrantlock de la feria. Si es justo es cierto, significa que es un bloqueo justo, y si es justo es falso, significa que es un bloqueo no fair. Reentrantlock (boolean fair) // consulta el número de veces que el hilo actual ha mantenido este bloqueo. int getholdCount () // Devuelve el hilo que actualmente posee este bloqueo, y si este bloqueo no es propiedad de ningún hilo, regrese nulo. El hilo protegido getowner () // Devuelve una colección que contiene el hilo que puede estar esperando para adquirir este bloqueo. Colección protegida <Shift> GetQueueDThreads () // Devuelve el número estimado de hilos que esperan para adquirir este bloqueo. int getqueuelgth () // Devuelve una colección que contiene aquellos hilos que pueden estar esperando una condición determinada relacionada con este bloqueo. Colección protegida <Shuste> GetWaitingThreads (condición de condición) // Devuelve la estimación de subprocesos esperando la condición dada asociada con este bloqueo. int getwaitqueuel longitud (condición condición) // consulta si el hilo dado está esperando adquirir este bloqueo. Boolean HasqueedThread (hilo de hilo) // consulta si algunos hilos están esperando para adquirir este bloqueo. Boolean HasqueedThreads () // consulta si algunos hilos están esperando una condición dada relacionada con este bloqueo. Haders booleanos (condición condición) // Devuelve verdadero si es "bloqueo justo", de lo contrario regresa falso. Boolean isfair () // consulta si el hilo actual mantiene este bloqueo. boolean isheldbycurrentThread () // consulta si este bloqueo está en poder de algún hilo. boolean islocked () // Obtén el bloqueo. Blok () void () // Si el hilo actual no se interrumpe, se adquiere el bloqueo. Void LockInterruption () // Devuelve la instancia de condición utilizada para usar con esta instancia de bloqueo. Condición newcondition () // Solo adquiere el bloqueo si no lo sostiene otro hilo durante la llamada. boolean trylock () // Si el bloqueo no está sostenido por otro hilo dentro de un tiempo de espera dado y el hilo actual no se interrumpe, el bloqueo se adquiere. Boolean Trylock (tiempo de espera largo, unidad de tiempo de tiempo) // Intenta liberar este bloqueo. desbloqueo vacío ()
2. Ejemplo de reentrantlock
Al comparar "Ejemplo 1" y "Ejemplo 2", podemos entender claramente el papel de bloqueo y desbloqueo
2.1 Ejemplo 1
import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; // Locktest1.java// Repository Class Depot {private int size; // El número real de bloqueo de bloqueo privado de repositorio; // Lock Public Depot () {this.size = 0; this.lock = new ReentrantLock (); } public void product (int val) {Lock.lock (); intente {size += val; System.out.printf ("%s product (%d) -> size =%d/n", hilo.currentThread (). GetName (), val, tamaño); } Finalmente {Lock.unlock (); }} consumo público vacío (int val) {Lock.lock (); intente {size -= val; System.out.printf ("%s de consumo (%d) <- size =%d/n", hilo.currentThread (). GetName (), val, tamaño); } Finalmente {Lock.unlock (); }}}; // Productor Clase Productor {depósito de depósito privado; Productor público (Depot Depot) {this.depot = depósito; } // Productos de consumo: cree un nuevo hilo para producir productos en el almacén. public void product (final int val) {new Thread () {public void run () {deposit.produce (val); } }.comenzar(); }} // Customer de clase de consumo {depósito de depósito privado; Cliente público (Depot Depot) {this.depot = depósito; } // Producto del consumidor: cree un nuevo hilo para consumir el producto del almacén. Public void Consume (final int val) {new Thread () {public void run () {depot.consume (val); } }.comenzar(); }} public class LockTest1 {public static void main (string [] args) {depot mDepot = new Depot (); Productor mPro = nuevo productor (mdepot); Cliente MCUS = nuevo Cliente (MDepot); mPro.ProDuce (60); mPro.ProDuce (120); McUS.Consume (90); MCUS.Consume (150); mPro.ProDuce (110); }} Resultados de ejecución:
Thread-0 Produce (60)-> Size = 60Thread-1 Produce (120)-> size = 180Thread-3 Consume (150) <-size = 30Thread-2 Consume (90) <-size = -60Thread-4 producir (110)-> size = 50
Análisis de resultados:
(1) El depósito es un almacén. Los bienes se pueden producir en el almacén a través de productos (), y los bienes en el almacén se pueden consumir a través del consumo (). El acceso mutuamente excluyente al almacén se logra a través del bloqueo exclusivo de bloqueo: antes de operar los productos en el almacén (producción/consumo), el almacén se bloqueará primero a través del bloqueo (), y luego se desbloqueará a través del desbloqueo () después de que se complete la operación.
(2) El productor es un productor. Llamar a la función Produce () en el productor puede crear un nuevo hilo para producir productos en el almacén.
(3) El cliente es una categoría de consumidor. Llamar a la función de consumo () en el cliente puede crear un nuevo producto de consumo de subprocesos en el almacén.
(4) En el hilo principal principal, crearemos un nuevo productor MPRO y un nuevo MCU del consumidor. Producen/productos de consumo en almacenes respectivamente.
Según la cantidad de producción/consumo en Main, el producto restante final en el almacén debe ser de 50. ¡Los resultados de la operación están en línea con nuestras expectativas!
Hay dos problemas con este modelo:
(1) En realidad, la capacidad del almacén no puede ser negativa. Sin embargo, la capacidad del almacén en este modelo puede ser negativa, ¡lo que contradice la realidad!
(2) En realidad, la capacidad del almacén es limitada. Sin embargo, ¡realmente no hay límite para la capacidad en este modelo!
Hablaremos brevemente sobre cómo resolver estos dos problemas. Ahora, echemos un vistazo a un simple ejemplo 2 primero; Al comparar "Ejemplo 1" y "Ejemplo 2", podemos entender el propósito de bloquear () y desbloquear () más claramente.
2.2 Ejemplo 2
import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; // Locktest2.java// Repository Class Depot {private int size; // El número real de bloqueo de bloqueo privado de repositorio; // Lock Public Depot () {this.size = 0; this.lock = new ReentrantLock (); } public void product (int val) {// lock.lock (); // try {size += val; System.out.printf ("%s product (%d) -> size =%d/n", thread.currentThread (). GetName (), val, size); //} capt (interruptedException e) {//} finalmente {// lister.unlock (); //}} public void consumo (int val) {// lock.lock (); // trey {trey {Val; System.out.printf ("%s consumo (%d) <- size =%d/n", thread.currentThread (). GetName (), val, size); //} finalmente {// lister.unlock (); //}}}; // productor de clases {depósito de depósito privado; Productor público (depósito de depósito) {this.depot = depósito; } // Producto del consumidor: cree un nuevo hilo para producir el producto en el almacén. public void product (final int val) {new Thread () {public void run () {deposit.produce (val); } }.comenzar(); }} // Customer de clase de consumo {depósito de depósito privado; Cliente público (Depot Depot) {this.depot = depósito; } // Producto del consumidor: cree un nuevo hilo para consumir el producto del almacén. Public void Consume (final int val) {new Thread () {public void run () {depot.consume (val); } }.comenzar(); }} public class LockTest2 {public static void main (string [] args) {depot mdepot = new Depot (); Productor mPro = nuevo productor (mdepot); Cliente MCUS = nuevo Cliente (MDepot); mPro.ProDuce (60); mPro.ProDuce (120); McUS.Consume (90); MCUS.Consume (150); mPro.ProDuce (110); }} (Una vez) resultado:
Thread-0 Produce (60)-> size = -60Thread-4 product (110)-> size = 50Thread-2 consumo (90) <-size = -60Thread-1 product (120)-> size = -60Thread-3 consumo (150) <-size = -60
Descripción de los resultados:
"Ejemplo 2" elimina el bloqueo de bloqueo basado en "Ejemplo 1". En el ejemplo 2, el producto restante final en el almacén es -60, no los 50 que esperábamos. La razón es que no implementamos acceso mutex al repositorio.
2.3 Ejemplo 3
En "Ejemplo 3", utilizamos la condición para resolver dos problemas en el "Ejemplo 1": "La capacidad del almacén no puede ser negativa" y "la capacidad del almacén es limitada".
La solución a este problema es a través de la condición. La condición debe usarse junto con el bloqueo: el método Await () en la condición puede hacer que el hilo bloquee [similar a Wait ()]; El método de condición Signal () puede hacer que el hilo de despertar se vea [similar a notificar ()].
import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; import java.util.concurrent.locks.condition; // locktest3.java// Warehouse Class Depot {private int capacidad; // Capacidad de almacén Tamaño privado int; // El número real de bloqueo privado de almacén; // Lock Lock Exclusive Condición privada FullCondition; // Condiciones de producción Condiciones privadas Cacondición vacía; // Condiciones de consumo Public Depot (int capacidad) {this.capacity = capacidad; this.size = 0; this.lock = new ReentrantLock (); this.fullCondtion = Lock.NewCondition (); this.emptycondition = lock.newcondition (); } public void product (int val) {Lock.lock (); Pruebe {// izquierda significa "la cantidad que desea producir" (puede ser demasiada producción, por lo que debe producir más) int Left = val; Mientras (izquierda> 0) {// Cuando el inventario esté lleno, espere a que el "consumidor" consuma el producto. while (size> = capacidad) fullCondtion.Await (); // Obtenga "cantidad de producción real" (es decir, la nueva cantidad agregada en el inventario) // si "Inventario" + "Cantidad de producción deseada"> "Capacidad total", luego "Incremento real" = "Capacidad total" - "Capacidad actual". (Complete el almacén en este momento) // de lo contrario "incremento real" = "la cantidad que desea producir" int inc = (tamaño+izquierda)> capacidad? (tamaño de capacidad): izquierda; tamaño += inc; Izquierda -= inc; System.out.printf ("%s product (%3d) -> izquierda =%3d, inc =%3d, tamaño =%3d/n", thread.currentThread (). GetName (), val, izquierda, inc, tamaño); // Notificar al "consumidor" que puede consumir. vacíacondtion.signal (); }} capt (interruptedException e) {} finalmente {Lock.unlock (); }} consumo público vacío (int val) {Lock.lock (); Pruebe {// izquierda significa "la cantidad de consumo a consumir" (puede ser demasiado grande, el inventario no es suficiente, por lo que debe consumir más) int Left = val; mientras (izquierda> 0) {// Cuando el inventario es 0, espere a que el "productor" produzca el producto. while (size <= 0) EmptyCondtion.Await (); // Obtenga "cantidad de consumo real" (es decir, la disminución real en el inventario) // si "inventario" <"la cantidad que el cliente quiere consumir", luego "consumo real" = "inventario"; // de lo contrario, "consumo real" = "la cantidad que el cliente quiere consumir". int dec = (tamaño <izquierdo)? Tamaño: izquierda; tamaño -= dec; Izquierda -= dec; System.out.printf ("%s consumo (%3d) <- izquierda =%3d, dec =%3d, tamaño =%3d/n", thread.currentThread (). GetName (), val, izquierda, dec, tamaño); FullCondtion.signal (); }} capt (interruptedException e) {} finalmente {Lock.unlock (); }} public String toString () {return "Capacidad:"+capacidad+", tamaño real:"+tamaño; }}; // productor de clase productor {depósito de depósito privado; Productor público (depósito de depósito) {this.depot = depósito; } // Producto del consumidor: cree un nuevo hilo para producir el producto en el almacén. public void product (final int val) {new Thread () {public void run () {deposit.produce (val); } }.comenzar(); }} // Customer de clase de consumo {depósito de depósito privado; Cliente público (Depot Depot) {this.depot = depósito; } // Producto del consumidor: cree un nuevo hilo para consumir el producto del almacén. Public void Consume (final int val) {new Thread () {public void run () {depot.consume (val); } }.comenzar(); }} public class LockTest3 {public static void main (string [] args) {depot mdepot = new Depot (100); Productor mPro = nuevo productor (mdepot); Cliente MCUS = nuevo Cliente (MDepot); mPro.ProDuce (60); mPro.ProDuce (120); McUS.Consume (90); MCUS.Consume (150); mPro.ProDuce (110); }} (Una vez) resultado:
Hilt-0 product (60)-> izquierda = 0, inc = 60, tamaño = 60Thread-1 product (120)-> izquierda = 80, inc = 40, tamaño = 100Thread-2 consumo (90) <-izquierda = 0, dec = 90, tamaño = 10Thread-3 Consumo (150) <-Left = 140, dec = 10, tamaño = 0Thread-4 Produce (110)-> Left = 10, Inc = 100, size de 1000, dec = 10, tamaño = 0Thread-4 Produce (110)-> Izquierda = 10, Inc = 100, 100). consumo (150) <-izquierda = 40, dec = 100, size = 0Thread-4 product (110)-> izquierda = 0, inc = 10, tamaño = 10thread-3 consumo (150) <-izquierda = 30, dec = 10, size = 0thread-1 producir (120)-> izquierda = 0, inc = 80, tamaño = 80thread-3 consumo (150) <-izquierda = 0, dec = 30, size size