Bloqueos en Java: sincronizados, bloqueados, volátiles, CAS, paquete concurrente

Continuará. . .

Este artículo explora principalmente los bloqueos en Java, que incluyen:

Sincronizado debe entenderse a partir de los siguientes tres aspectos

  • [Rendimiento del código] [Actualización del bloqueo] [Implementación del bloqueo en diferentes niveles del sistema]

  • 1. Rendimiento del código

    • (1) Objeto de bloqueo sincronizado y método de sincronización: solo válido para ese objeto (bloqueo de objeto: bloquear este o bloquear otros objetos); consulte la diferencia entre bloqueo de método sincronizado, bloqueo de objeto y bloqueo de clase para obtener más detalles
    • (2) Clases de bloqueo sincronizadas y métodos estáticos: tienen efecto para todos los objetos (bloqueo de clase: estático, object.class).
    • (3) Si se produce una ejecución mutuamente excluyente (bloqueo), el juicio principal es si es el mismo bloqueo o no. \ Color {red} {El juicio principal es si es el mismo bloqueo o no}Señor a juzgar fuera es no que diferente Yi poner una cerradura en la línea , tales como una clase de dos métodos, un bloqueo estático, un bloqueo no estático, entonces la obstrucción no va a suceder, porque no es la misma cerradura, tenga en cuenta que la clase de objeto de clase También es un objeto en el análisis final.
  • 2. Bloquear actualización

    • Cuando se creó el objeto por primera vez, la palabra clave del encabezado del objeto no almacenaba la información de bloqueo y contenía el código hash del objeto.
    • Sin bloqueo -> bloqueo parcial: almacena el ID del hilo en la palabra clave del encabezado del objeto.
    • Bloqueo parcial -> Bloqueo ligero: los subprocesos en competencia utilizan el método CAS para almacenar el objeto LR (Registro de bloqueo) en la pila de subprocesos en la palabra clave del encabezado del objeto. En este momento, el código hash del objeto existe en el LR de la pila de subprocesos.
    • Bloqueo ligero -> Bloqueo pesado:
      • Operación: Vaya al sistema operativo para solicitar un bloqueo.En este momento, el estado cambia del modo de usuario al modo de kernel.
      • Condiciones de actualización: gire más de una determinada cantidad de veces (como 10 veces) o gire los hilos más de la mitad del número de núcleos de CPU. Mejorado después de 1.6 y agregado giro adaptativo.
    • Por qué introducir bloqueos pesados: porque la operación CAS de bloqueos ligeros consume bastante recursos de la CPU.
  • 3. La realización de cerraduras en diferentes niveles del sistema.

    • capa java: palabra clave: sincronizada
    • .class layer: monitorenter y monitorexit
    • Estado de funcionamiento: actualización automática del bloqueo, eliminación del bloqueo, engrosamiento del bloqueo, análisis de escape
    • Capa de ensamblaje: lockxchg

Necesidades volátiles para llegar al fondo

  • ¿Qué pueden hacer los volátiles?

    • 1. Asegurar la visibilidad de esta variable para todos los hilos.
    • 2. Prohibir la optimización de reordenamiento de instrucciones
  • ¿Cómo garantiza volatile la visibilidad de esta variable para todos los hilos?

    • Comprensión 1: cuando la CPU modifica los datos, primero modifica la caché y luego se sincroniza con la memoria principal. Al sincronizar de nuevo con la memoria principal, si otras CPU también almacenan en caché estos datos, los datos de la otra caché de la CPU se volverán inválidos (a través de Oler la propagación de los datos del bus y comprobar si se ha modificado la dirección de la memoria principal correspondiente a la caché. De esta forma, cuando otras CPU van a su caché para leer los datos y encuentran que la caché no es válida, se debe recuperar de la memoria principal.
    • Comprensión 2: El uso de la palabra clave volátil obligará a que el valor modificado se escriba en la memoria principal inmediatamente;
      • Usando la palabra clave volátil, cuando se modifica el hilo 2, la línea de caché de la variable de caché parada en la memoria de trabajo del hilo 1 no será válida. \ Color {red} la línea de caché no es válidaLa línea de memoria caché no tiene efecto (reflejada en la capa de hardware, se invalida la caché L1 o L2 de la CPU en la línea de caché correspondiente); debido a la memoria de trabajo no válida del hilo 1 en la línea de caché de parada de variable de caché, el hilo 1 lee la variable de parada de nuevo Irá a la memoria principal para leer
  • Dado que lo volátil puede garantizar la visibilidad, ¿por qué no puede garantizar la atomicidad?

    • Por qué lo volátil puede garantizar el orden pero no la atomicidad
    • Cuando i = 5, los dos hilos A y B leen el valor de i al mismo tiempo, y luego el hilo A realiza la operación de temp = i + 1. Cabe señalar que el valor de i no ha cambiado en este momento, y luego el hilo B también Se realiza la operación de temp = i + 1. Tenga en cuenta que en este momento, el valor de i guardado por los dos subprocesos A y B es 5, y el valor de temp es 6. Luego, el subproceso A realiza la operación de i = temp (6), En este momento, el valor de i se actualizará inmediatamente en la memoria principal y notificará a otros subprocesos que el valor de i guardado por otros subprocesos no es válido. En este momento, el subproceso B debe leer el valor de i nuevamente. En este momento, el valor de i guardado por el subproceso B es 6, y la temperatura guardada por el subproceso B sigue siendo Sigue siendo 6, y luego el hilo B ejecuta i = temp (6), por lo que el resultado del cálculo es 1 menos de lo esperado.
  • Qué tan volátil evita la optimización del reordenamiento de instrucciones

    • capa java: agregue la palabra clave volátil
    • Capa de código de bytes: ACC_VOLATILE
    • Capa JVM: agregar barrera de memoria
    • capa de punto de acceso: bus de bloqueo
  • ¿Qué es la barrera de la memoria?

    • Es una instrucción que controla el valor agregando las barreras correspondientes antes y después de cada operación de lectura y escritura volátil.
  • ¿Por qué agregar volátiles a las cerraduras DCL?

    • El problema de DCL aparece en un objeto nuevo llamado new, que no es una operación atómica. Hay reordenamiento de instrucciones. El conflicto existe en varios subprocesos. Cuando se crea un objeto, se reorganizan las instrucciones, pero el objeto no tiene init y otro subproceso llama al objeto. Error de objeto no inicializado.
    • ¿Por qué el modo DCL singleton necesita agregar volátiles?
  • ¿Volátil tiene un candado?

    • Sí, su implementación en la capa de compilación está bloqueada
  • Escenarios de aplicación de volátiles

    • 1. En el escenario de operación atómica, se pueden usar volátiles en lugar de otras cerraduras, que es más eficiente

Conceptos básicos en concurrencia

  • Dos reglas

    • Regla como si fuera en serie: La regla como si fuera en serie significa que no importa cómo se reordenen (compilador y procesador para aumentar el grado de paralelismo), el resultado del programa (un solo hilo) no se puede cambiar. Esta es la semántica que deben cumplir el compilador, el tiempo de ejecución y el procesador.
    • Regla de sucede antes: ciertas operaciones deben distinguir estrictamente el orden de ejecución, como el inicio y la detención del hilo, agregar desbloqueo, volátil
  • Protocolo de coherencia de caché

    • Cuando la CPU escribe datos, si encuentra que la variable manipulada es una variable compartida, es decir, existe una copia de la variable en otras CPU, enviará una señal para notificar a otras CPU para invalidar la línea de caché de la variable, por lo que cuando otras CPU necesiten leer Cuando recupera esta variable, encuentra que la línea de caché que almacena la variable en caché no es válida, luego la leerá de la memoria nuevamente.
  • Principio de localidad del programa.

    • Localidad temporal: si se accede a un elemento de información, es probable que se vuelva a acceder en un futuro próximo. Tales como bucles, recursividad, llamadas repetidas de métodos, etc.
    • Localidad espacial: si se hace referencia a una ubicación de memoria, también se hará referencia a la ubicación cercana en el futuro.
  • Línea de caché

    • Cada caché se compone de líneas de caché y el sistema de caché se almacena en unidades de líneas de caché. Una línea de caché es una potencia entera de 2 bytes consecutivos, generalmente 32-256 bytes.
    • El tamaño de línea de caché más común es de 64 bytes . Cuando varios subprocesos modifican variables independientes, si estas variables comparten la misma línea de caché, afectarán inadvertidamente el rendimiento de los demás, que es pseudocompartir

CAS y AQS en concurrencia

  • 【CASO】
  • 【AQS】

El paquete Concurrent analiza principalmente estas categorías

  • 【Cerraduras】

    • ReentrantLock
    • ReadWriteLock
  • 【atómico】

  • 【BlockingQueue】

  • CountDownLatch

    • Rol: clase de herramienta de control multiproceso, gestión unificada de múltiples subprocesos para alcanzar un cierto estado.
    • Método principal:
      • CountDownLatch (int count) // Crea una instancia de un contador regresivo, el conteo especifica el número de conteos
      • countDown () // cuenta regresiva de uno en uno
      • await () // Espera, cuando la cuenta se reduce a 0, todos los hilos se ejecutan en paralelo
      • Agregue latch.countDown () al final de cada método. Cuando el valor del contador se convierte en 0, el hilo de await () en CountDownLatch se despertará.
    • Uso típico de CountDownLatch:
      • 1. Cierto subproceso espera a que n subprocesos terminen de ejecutarse antes de comenzar a ejecutarse. Inicialice el contador de CountDownLatch a un nuevo CountDownLatch (n) y disminuya el contador en 1 cada vez que un subproceso de tarea termine de ejecutar countdownLatch.countDown (). Cuando el valor del contador sea 0, el subproceso de await () en CountDownLatch Se despertó. Un escenario de aplicación típico es que cuando se inicia un servicio, el subproceso principal debe esperar a que se carguen varios componentes antes de continuar.
      • 2. Obtenga el máximo paralelismo cuando varios subprocesos comiencen a ejecutar tareas. Tenga en cuenta que se trata de paralelismo, no de simultaneidad. Enfatiza que varios subprocesos comienzan a ejecutarse al mismo tiempo. Al igual que en una carrera, se colocan varios hilos en el punto de partida, esperando a que suene el pistoletazo de salida y luego se ejecutan al mismo tiempo. El método consiste en inicializar un CountDownLatch (1) compartido, inicializar su contador a 1, varios subprocesos primero countdownlatch.await () antes de comenzar a realizar tareas, cuando el subproceso principal llama a countDown (), el contador se vuelve 0 y varios subprocesos Se despertó al mismo tiempo.
    • Nota:
      CountDownLatch es un uso único. El valor de la calculadora solo se puede inicializar una vez en el método de construcción. Después de eso, no hay ningún mecanismo para establecer el valor nuevamente. Después de que se usa CountDownLatch, no se puede usar nuevamente.
    • Blog de referencia: comprensión y uso de CountDownLatch
  • CyclicBarrier

    • Función: controla el punto final unificado de multiproceso, similar al reciclable CountDownLatch
    • Método principal:
      • esperar()
    • Precauciones:
      • El hilo llama a await () para indicar que ha alcanzado la cerca
      • BrokenBarrierException indica que la barrera ha sido destruida. La causa del daño puede ser que uno de los hilos se interrumpió o se agotó el tiempo de espera ()
    • Blog de referencia: uso detallado de CyclicBarrier
  • 【Semáforo】

Supongo que te gusta

Origin blog.csdn.net/ljfirst/article/details/108039297
Recomendado
Clasificación