Bloqueo de bloqueo

1. Por qué bloquear

  1. ¿Por qué no se sincroniza lo suficiente, pero también necesita bloqueo?

       Los dos bloqueos más comunes, bloqueo y sincronizado, pueden lograr el propósito de seguridad de subprocesos, pero sus funciones son muy diferentes.

       El bloqueo no se usa para reemplazar sincronizado sino para proporcionar funciones avanzadas cuando el uso sincronizado no se ajusta a la situación o es inapropiado

  1. ¿Por qué la sincronización no es suficiente?
  • Baja eficiencia : la liberación de bloqueos es poco común, el tiempo de espera no se puede establecer cuando se intenta obtener el bloqueo y un hilo que está tratando de obtener el bloqueo no se puede interrumpir
  • No lo suficientemente flexible : cuando el bloqueo y la liberación son únicos, es posible que una sola condición para cada bloqueo no sea suficiente
  • No se puede saber si el bloqueo se adquirió con éxito

2. El significado de Lock

  1. En comparación con el uso de declaraciones y métodos sincronizados, la implementación de Lock proporciona una gama más amplia de operaciones de bloqueo. Permiten una estructura más flexible, pueden tener propiedades completamente diferentes y pueden admitir múltiples objetos de condición asociados.
  2. Lock es una herramienta que se utiliza para controlar el acceso a recursos compartidos por varios subprocesos. Generalmente, los bloqueos brindan acceso exclusivo a los recursos compartidos. Solo un subproceso puede adquirir el bloqueo a la vez, y todos los accesos a los recursos compartidos deben adquirir el bloqueo primero . Sin embargo, algunos bloqueos pueden permitir el acceso simultáneo a recursos compartidos , como el bloqueo de lectura de ReadWriteLock.
  3. Utilice el método sincronizado o la instrucción para acceder al bloqueo de monitor implícito asociado con cada objeto, pero obligará a que todos los bloqueos se adquieran y liberen en una estructura de bloque. Al adquirir múltiples candados, deben liberar los candados en el orden inverso.
  4. Aunque el mecanismo de alcance utilizado para los métodos y declaraciones sincronizados facilita la programación con bloqueos de monitor y ayuda a evitar muchos errores de programación comunes que involucran bloqueos, en algunos casos es necesario ser más flexible para usar bloqueos. Por ejemplo, algunos algoritmos que se utilizan para atravesar estructuras de datos a las que se accede simultáneamente necesitan utilizar "traspaso" o "bloqueo de cadena": adquieres el bloqueo del nodo A, luego adquieres el bloqueo del nodo B, luego liberas A y adquieres C, luego liberas B y obtener D, etc. La implementación de la interfaz Lock habilita este tipo de tecnología al permitir adquirir y liberar cerraduras en diferentes ámbitos y permitir la adquisición y liberación de múltiples cerraduras en cualquier orden.

3. El uso de la cerradura

       Una mayor flexibilidad conlleva responsabilidades adicionales. La falta de un bloqueo de estructura de bloque requiere la liberación manual del bloqueo. En la mayoría de los casos, se deben utilizar los siguientes modismos:

Lock lock = new ReentrantLock();
lock.lock();
try{
    
    
  
}finally {
    
    
  lock.unlock();
}

       Cuando el bloqueo y el desbloqueo ocurren en diferentes rangos, se debe tener cuidado para garantizar que todo el código ejecutado mientras se mantiene el candado esté protegido por try-finalmente o try-catch para garantizar que el bloqueo se libere cuando sea necesario.
       La implementación de Lock utiliza un intento sin bloqueo de adquirir un bloqueo (tryLock ()) y un intento de adquirir un bloqueo interrumpible (lockInterruptiblemente y un intento de adquirir un bloqueo), proporcionando más funciones que usando métodos y declaraciones sincronizados. Puede que se agote el tiempo de espera (tryLock (long, TimeUnit)).

       La clase Lock también puede proporcionar comportamientos y semánticas completamente diferentes del bloqueo de monitor implícito, como orden garantizada, detección no reutilizable o de interbloqueo. Si la implementación proporciona esta semántica especial, la implementación debe documentar esta semántica.

       Tenga en cuenta que las instancias de Lock son solo objetos ordinarios y que ellos mismos pueden usarse como destinos en declaraciones sincronizadas. Obtener el bloqueo del monitor de una instancia de Lock no tiene una relación específica con llamar a ningún método de bloqueo de esa instancia. Se recomienda evitar confusiones y no utilizar las instancias de bloqueo de esta forma a menos que las utilice en su propia implementación.

4. Sincronización de memoria

       Todas las implementaciones de Lock deben aplicar la misma semántica de sincronización de memoria proporcionada por el bloqueo de monitor integrado, como se describe en la especificación del lenguaje Java:

  • Una operación de bloqueo exitosa tiene el mismo efecto de sincronización de memoria que una operación de bloqueo exitosa.
  • Una operación de desbloqueo exitosa tiene el mismo efecto de sincronización de memoria que una operación de desbloqueo exitosa.

       Las operaciones de bloqueo y desbloqueo fallidas y las operaciones de bloqueo / desbloqueo de reentrada no requieren ningún efecto de sincronización de memoria.

Consideraciones de implementación

       Las tres formas de adquisición de bloqueo (interrumpible, ininterrumpible y temporizado) pueden diferir en sus características de rendimiento. Además, en una clase de bloqueo determinada, es posible que no se proporcione la capacidad de interrumpir un bloqueo en curso. Por lo tanto, no es necesario definir exactamente la misma garantía o implementación semántica para las tres formas de adquisición de bloqueo, y no es necesario admitir la interrupción de la adquisición de bloqueo en curso. Se necesita una implementación para documentar claramente la semántica y las garantías proporcionadas por cada método de bloqueo. Dentro del alcance de soportar la interrupción de adquisición de bloqueo, también debe obedecer la semántica de interrupción definida en esta interfaz: todo o solo durante la entrada del método .

5.La interfaz proporcionada por Lock

imagen

5.1 Adquirir un candado

void lock(); // 获取锁。
  1. La forma más común de adquirir un bloqueo es esperar si el bloqueo es adquirido por otros subprocesos
  2. El bloqueo no liberará automáticamente el bloqueo cuando sea anormal como sincronizado
  3. Por lo tanto, el bloqueo debe liberarse finalmente para garantizar que el bloqueo debe liberarse cuando ocurra una excepción.

Nota : el método lock () no se puede interrumpir, lo que traerá grandes peligros ocultos: una vez que se bloquea, lock () caerá en un estado de espera permanente

5.2 Adquirir un bloqueo de interrupción

void lockInterruptibly() throws InterruptedException;

       A menos que se interrumpa el hilo actual, se adquiere el bloqueo.
       Adquiera el candado (si lo hubiera) y regrese de inmediato.

       Si el bloqueo no está disponible, para propósitos de programación de subprocesos, el subproceso actual se suspenderá y dormirá antes de que ocurra una de las dos situaciones siguientes:

  • El bloqueo es adquirido por el hilo actual;
  • Algunos otros hilos interrumpen el hilo actual y admiten la interrupción de la adquisición de bloqueo.

       Si el subproceso actual: su estado de interrupción se ha establecido al ingresar a este método; o se interrumpe cuando se adquiere el bloqueo, y se admite la interrupción de la adquisición del bloqueo , se lanza una InterruptedException y se borra el estado de interrupción del subproceso actual .

Precauciones

       En algunas implementaciones, la capacidad de interrumpir la adquisición de bloqueos puede ser imposible y, si es posible, puede ser una operación costosa. El programador debe saber que este puede ser el caso. En este caso, se debe documentar la implementación. La implementación puede preferir responder a las interrupciones en comparación con regresar de un método normal. Las implementaciones de bloqueo pueden detectar el uso incorrecto del bloqueo, como las llamadas que pueden causar un interbloqueo y, en este caso, pueden generar excepciones (sin marcar).

Tenga en cuenta que la sincronización es ininterrumpida al adquirir la cerradura

5.3 Intento de adquirir bloqueo

boolean tryLock();

       Adquiera el candado (si lo hay) sin bloqueo y devuélvalo a verdadero de inmediato. Si el bloqueo no está disponible, este método devolverá falso inmediatamente. Comparado con el método Lock, obviamente es más poderoso. Podemos determinar el comportamiento del programa posterior en función de si se puede obtener el bloqueo.
Nota: Este método regresará inmediatamente, incluso si el bloqueo no está disponible, no será allí espera

El uso típico de este método es:

Lock lock = new ReentrantLock();
if(lock.tryLock()){
    
    
  try{
    
    
    // TODO
  }finally {
    
    
    lock.unlock();
  }
}else{
    
    
  // TODO
}

5.4 Adquirir un candado dentro de un cierto período de tiempo

boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

       Si el hilo adquiere el bloqueo dentro del tiempo de espera dado y el hilo actual no se ha interrumpido, se adquiere el bloqueo.
       Si el bloqueo está disponible, este método devuelve inmediatamente un valor verdadero. Si el bloqueo no está disponible, para fines de programación de subprocesos, el subproceso actual se suspenderá y dormirá hasta que ocurra una de las siguientes tres situaciones:

  1. El bloqueo es adquirido por el hilo actual.
  2. Algunos otros hilos interrumpirán el hilo actual y admitirán la interrupción de la adquisición de bloqueos.
  3. Si el bloqueo se adquiere después del tiempo de espera especificado, el valor devuelto es verdadero.

       Si ha transcurrido el tiempo de espera especificado, el valor devuelto es falso. Si el tiempo es menor o igual a cero, el método no esperará en absoluto .

Precauciones

       En algunas implementaciones, la capacidad de interrumpir la adquisición de bloqueos puede ser imposible y, si es posible, puede ser una operación costosa. El programador debe saber que este puede ser el caso. En este caso, se debe documentar la implementación. Las implementaciones pueden preferir responder a las interrupciones que los métodos normales que regresan o reportan tiempos de espera. Las implementaciones de bloqueo pueden detectar el uso incorrecto del bloqueo, como las llamadas que pueden causar un interbloqueo y, en este caso, pueden generar excepciones (sin marcar).

5.5 Desbloquear

void unlock(); //释放锁。

Nota Las
       implementaciones de bloqueo generalmente restringen qué subprocesos pueden liberar el bloqueo (generalmente solo el titular del bloqueo puede liberar el bloqueo), y si se viola esta restricción, se puede generar una excepción (sin marcar).

5.6 Obtener el componente de notificación en espera

Condition newCondition(); //返回绑定到此Lock实例的新Condition实例。

       Este componente está vinculado al bloqueo actual y el hilo actual solo ha adquirido el bloqueo. Se puede llamar al método wait () del componente y, después de la llamada, el hilo actual liberará el bloqueo.
Precauciones

El funcionamiento exacto de la instancia de Condition depende de la implementación de Lock.

5.7 Resumen

       El bloqueo de objeto de bloqueo también proporciona otras características de sincronización que sincronizado no tiene, como la adquisición de bloqueos interrumpibles (sincronizado no se puede interrumpir mientras se espera para adquirir el bloqueo), la adquisición de bloqueos de interrupción de tiempo de espera y la variable de condición múltiple Condición en espera para el mecanismo de activación, lo que también hace que la cerradura Lock sea más flexible. El bloqueo y la liberación de bloqueos tienen la misma semántica de memoria que los sincronizados, lo que significa que después de que se bloquea el siguiente hilo, se pueden ver todas las operaciones que ocurrieron antes de que se desbloqueara el hilo anterior.

6. Clasificación de cerraduras

Según las siguientes 6 situaciones, se pueden distinguir una variedad de cerraduras diferentes, que se describen en detalle a continuación

6.1 ¿Quiere bloquear los recursos de sincronización?

Esta bloqueado Nombre de bloqueo Método para realizar ejemplo
Poste de bloqueo Bloqueo pesimista sincronizado 、 bloqueo sincronizado 、 bloqueo
No está bloqueado Cerradura optimista Algoritmo CAS Clase atómica, contenedor concurrente

Los bloqueos pesimistas también se denominan bloqueos de sincronización mutuamente excluyentes. Las desventajas de los bloqueos de sincronización mutuamente excluyentes:

  1. Desventajas de rendimiento causadas por el bloqueo y la activación
  2. Bloqueo permanente: si el hilo que sostiene el bloqueo está bloqueado permanentemente, como bucles infinitos, interbloqueos y otros problemas activos
  3. Inversión de prioridad

Bloqueo pesimista:

       Cuando un hilo adquiere el bloqueo, ningún otro hilo puede adquirir el bloqueo, y el bloqueo solo puede adquirirse después de que el hilo que sujeta el bloqueo lo libere.

Cerradura optimista:

       No habrá interferencia de otros hilos cuando esté operando usted mismo, por lo que el objeto no se bloqueará. Al actualizar, comparar mis datos durante el período de modificación y si alguien los ha modificado. Si no hay cambios, modifíquelos. Si se cambian, son de otra persona. Entonces no los cambiaré y me rendiré, o comenzaré de nuevo. .

Comparación de costos:

  1. El costo original de las cerraduras pesimistas es más alto que el de las cerraduras optimistas, pero la característica es que, de una vez por todas, incluso si la sección crítica mantiene las cerraduras durante más y más tiempo, no afectará el costo de las cerraduras mutex.
  2. El costo inicial del bloqueo pesimista es menor que el del bloqueo optimista, pero si el tiempo de giro es largo o si se reintenta constantemente, se consumirán más y más recursos.

escenas a utilizar:

  1. Bloqueo pesimista: adecuado para situaciones donde hay muchas escrituras concurrentes, adecuado para situaciones en las que la sección crítica tiene un tiempo de bloqueo relativamente largo, se pueden evitar bloqueos pesimistas, muchos giros inútiles y otros consumos
  2. Bloqueo optimista: adecuado para escenarios donde hay más lecturas simultáneas, ningún bloqueo puede mejorar en gran medida el rendimiento de lectura

6.2 ¿Se puede compartir un candado?

Ya sea para compartir Nombre de bloqueo
pueden Bloqueo compartido (bloqueo de lectura)
Hipocresía Cerradura exclusiva (cerradura exclusiva)

Cerradura compartida:

       Después de adquirir el bloqueo compartido, puede ver, pero no puede modificar ni eliminar los datos. Otros subprocesos también pueden adquirir el bloqueo compartido en este momento, y también pueden ver pero no pueden modificar ni eliminar los datos.

Caso: bloqueo de lectura ReentrantReadWriteLock (la implementación específica se explicará en una serie de artículos posteriores)

Cerradura exclusiva:

       Después de adquirir un bloqueo exclusivo, otros subprocesos no pueden adquirir el bloqueo actual, como un bloqueo de escritura.

Caso: bloqueo de escritura ReentrantReadWriteLock (la implementación específica se explicará en series de artículos posteriores)

6.3 Si hacer cola

Ya sea para hacer cola Nombre de bloqueo
cola Cerradura justa
Sin cola Bloqueo injusto

Bloqueo injusto:

       Primero intente saltar en la cola y luego no lo haga. Injusto significa que la cola no está completamente de acuerdo con el orden de la solicitud y puede saltarse bajo ciertas circunstancias.

El significado de la existencia:

  • Mejorar la eficiencia
  • Evite la brecha causada por el despertar

Caso:

  1. Tome ReentrantLock como ejemplo, el parámetro es falso al crear un objeto (la implementación específica se explicará en series posteriores de artículos)
  2. Para el método tryLock (), no cumple con las reglas justas establecidas

       Por ejemplo: cuando un subproceso ejecuta tryLock, una vez que un subproceso libera el bloqueo, el subproceso que está ejecutando tryLock puede adquirir inmediatamente el bloqueo incluso si hay otros subprocesos en la cola de espera antes que él.

Cerradura justa:

       Cola, equidad se refiere a la asignación de bloqueos en el orden de solicitud de subproceso

Caso: Tomando ReentrantLock como ejemplo, el parámetro es verdadero al crear el objeto (la implementación específica se explicará en series de artículos posteriores)

Nota:

       La injusticia tampoco promueve el comportamiento de saltar la cola. La injusticia aquí se refiere a saltar la cola en el momento adecuado, no a saltar ciegamente la cola.

Ventajas y desventajas:

Bloqueo injusto:

  • Ventajas: rendimiento más rápido y superior
  • Desventajas: posible falta de hilo

Cerradura justa:

  • Ventajas: los hilos son iguales, cada hilo tiene la posibilidad de ejecutarse en orden
  • Desventajas: rendimiento más lento y pequeño

6.4 ¿Se puede adquirir el mismo bloqueo repetidamente?

¿Es posible volver a entrar? Nombre de bloqueo
pueden Bloqueo reentrante
Hipocresía Cerradura no reentrada

Caso: Tome ReentrantLock como ejemplo (la implementación específica se explicará en series de artículos posteriores)

6.5 ¿Se puede interrumpir?

Puede ser interrumpido Nombre de bloqueo Caso
pueden Bloqueo interrumpible El bloqueo es un bloqueo interrumpible (porque tryLock y lockInterruptiblemente pueden responder a las interrupciones)
Hipocresía Cerradura ininterrumpida Sincronizado es un bloqueo ininterrumpido

6.6 El proceso de esperar el bloqueo

Ya sea para girar Nombre de bloqueo
si Bloqueo giratorio
No Bloqueo de bloqueo

escenas a utilizar:

  1. Los bloqueos de giro se utilizan generalmente para servidores de varios núcleos y son más eficientes que los bloqueos de bloqueo cuando la concurrencia no es muy alta.
  2. El bloqueo de giro es adecuado para el caso en el que la sección crítica es relativamente corta; de lo contrario, si la sección crítica es grande, una vez que la rosca obtiene el bloqueo, se liberará mucho tiempo después. También es inapropiado, porque desperdiciará rendimiento. al girar

7. Optimización de bloqueo

7.1 Optimización de bloqueo en máquina virtual

  1. Bloqueo giratorio
  2. Eliminación de bloqueo
  3. Engrosamiento de la cadena

Estos tres métodos de optimización de bloqueo se explican en el artículo anterior Sincronizado

7.2 Optimización de bloqueo al escribir código

  • Reducir el bloque de código de sincronización
  • Intenta no bloquear el método
  • Reducir el número de solicitudes de bloqueo
  • Evite los puntos calientes artificiales
  • Trate de no incluir candados en candados
  • Elija el tipo de bloqueo correcto o la herramienta adecuada

Supongo que te gusta

Origin blog.csdn.net/weixin_38071259/article/details/112541717
Recomendado
Clasificación