Simultaneidad sincronizada en profundidad

El uso
de Synchronized sincronizado siempre ha sido un papel veterano en la programación concurrente de subprocesos múltiples, y muchas personas lo llamarán un bloqueo de peso pesado. Sin embargo, con las diversas optimizaciones de sincronizado en Java SE 1.6, no es tan pesado en algunos casos. En Java SE 1.6, para reducir el costo de rendimiento de adquirir y liberar bloqueos, se introducen el bloqueo sesgado y el peso ligero. Magnitud cerradura, así como la estructura de almacenamiento y el proceso de actualización de la cerradura. Utilice la palabra clave sincronizada para modificar el método inc y ver el resultado de la ejecución. (Puede intentar eliminar el sincronizador usted mismo y ver si el resultado es 1000)

Demostración de clase pública {cuenta     privada estática int = 0;     public static void inc () {         sincronizado (Demo.class) {             try {                 Thread.sleep (1);             } catch (InterruptedException e) {                 e.printStackTrace ();             }             contar ++;          }     }     public static void main (String [] args) lanza InterruptedException {         for (int i = 0; i <1000; i ++) {             new Thread (() -> Demo.inc ()). start ();         }         Thread.sleep (3000);         System.out.println ("运行 结果" + recuento);      } } sincronizado 的 三种 应用 方式




















Hay tres formas de bloquear sincronizados, a saber

Método de instancia de modificación, actuar sobre la instancia actual para bloquear, obtener el bloqueo de la instancia actual antes de ingresar el código de sincronización
Método estático actuar sobre el bloqueo del objeto de clase actual, obtener el bloqueo del objeto de clase actual antes de ingresar el código de sincronización
Modificación bloque de código, especifique el objeto de bloqueo, bloquee el objeto dado y obtenga el bloqueo del objeto dado antes de ingresar a la base del código de sincronización.
El objeto detrás de los corchetes sincronizados
es un candado y cualquier objeto en Java puede convertirse en un candado. En pocas palabras, comparamos un objeto con una clave. El hilo que posee la clave puede ejecutar este método. Una vez obtenida la clave, en el proceso de ejecución del método, la clave se lleva consigo y solo hay una. Si el hilo posterior quiere acceder al método actual, porque no hay llave, no puede acceder a él y solo puede esperar en la puerta, y esperar a que el hilo anterior devuelva la llave. Por lo tanto, el objeto bloqueado sincronizado debe ser el mismo, si es un objeto diferente, significa que es la llave de una habitación diferente, lo que no tiene ningún efecto sobre el visitante.

Instrucciones bytecode sincronizadas
Utilice javap -v para visualizar las instrucciones bytecode del código correspondiente.Para la implementación del bloque de sincronización se utilizan las instrucciones monitorenter y monitorexit, que implícitamente realizan las operaciones Lock y UnLock para brindar garantía de atomicidad. La instrucción monitorenter se inserta al comienzo del bloque de código de sincronización y la instrucción monitorexit se inserta al final del bloque de código de sincronización JVM necesita asegurarse de que cada monitorenter tenga un monitorexit correspondiente.

Estas dos instrucciones son esencialmente para obtener el monitor de un objeto, este proceso es exclusivo, lo que significa que solo un hilo puede obtener el monitor del objeto protegido por sincronizado a la vez.

Cuando el hilo ejecuta la instrucción monitorenter, intentará adquirir la propiedad del monitor correspondiente al objeto, es decir, intentará adquirir el bloqueo del objeto; y ejecutar monitorexit es liberar la propiedad del monitor.

La disposición de los objetos en la memoria
En la máquina virtual Hotspot, la disposición de almacenamiento de los objetos en la memoria se puede dividir en tres áreas: Encabezado, Datos de instancia y Relleno. El encabezado del objeto contiene dos partes: la marca del objeto y la metainformación de la clase El encabezado del objeto Java es la base del objeto de bloqueo sincronizado. En términos generales, el objeto de bloqueo utilizado por sincronizado se almacena en el encabezado del objeto Java. Es la clave para cerraduras ligeras y cerraduras de deflexión.

Mawrk Word
Mark Word (marca de objeto) se utiliza para almacenar los datos de tiempo de ejecución del objeto en sí, como HashCode, edad de generación de GC, indicadores de estado de bloqueo, bloqueos retenidos por subprocesos, ID de subprocesos sesgados, marcas de tiempo sesgadas, etc. El encabezado del objeto Java generalmente ocupa dos códigos de máquina (en una máquina virtual de 32 bits, 1 código de máquina equivale a 4 bytes, que es de 32 bits)

Monitor
¿Qué es el Monitor?

1.Monitor es una herramienta que se utiliza para lograr la sincronización

2. Asociados con cada objeto Java, todos los objetos Java nacen con un monitor.

3.Monitor es la base para implementar Sychronized (bloqueo integrado)

El monitor del objeto es implementado por el objeto ObjectMonitor (C ++), y su estructura de datos relacionada con la sincronización es la siguiente:

ObjectMonitor () {     _count = 0; // Se usa para registrar el número de veces que el objeto es bloqueado por el hilo     _waiters = 0;     _recursions = 0; // El número de     bloqueos reentrantes_owner = NULL; // Apunta al hilo que contiene el ObjectMonitor      object_WaitSet = NULL; // Los subprocesos en estado de espera se agregarán a _WaitSet     _WaitSetLock = 0;     _EntryList = NULL; // Los subprocesos en el estado de bloqueo de bloqueo en espera se agregarán a la lista } El proceso de adquisición y actualización de bloqueo sincronizado primero Para comprender el concepto de bloqueos relacionados:









Spin lock (CAS): Deje que los subprocesos que no cumplan con las condiciones esperen un rato para ver si pueden obtener el bloqueo y evitar la sobrecarga causada por el cambio de subprocesos al ocupar el tiempo del procesador. Existe un límite en el tiempo o número de espera de giro, si el giro excede el tiempo definido y no se adquiere el bloqueo, se debe suspender el hilo. Después de JDK1.6, se introdujo un bloqueo de giro adaptativo. Adaptable significa que el número de giros no es fijo, sino que se basa en el tiempo del giro anterior en el mismo bloqueo y el estado del propietario del bloqueo. Decidido. Si en el mismo objeto de bloqueo, la espera de giro acaba de adquirir el bloqueo con éxito y el subproceso que mantiene el bloqueo se está ejecutando, entonces la máquina virtual pensará que es probable que este giro también tenga éxito nuevamente y permitirá el giro La espera dura durante un tiempo relativamente más largo. Si el giro rara vez se obtiene con éxito para un determinado bloqueo, es posible omitir el proceso de giro al intentar adquirir el bloqueo en el futuro y bloquear directamente el hilo para evitar desperdiciar recursos del procesador.

Bloqueo sesgado: en la mayoría de los casos, el mismo hilo siempre adquiere el bloqueo varias veces. Cuando un subproceso accede al bloque de sincronización y adquiere el bloqueo, el ID de subproceso del sesgo de bloqueo se almacena en el registro de bloqueo en el encabezado del objeto y el marco de pila. El bloqueo de sesgo es un bloqueo reentrante. Si la palabra de marca del encabezado del objeto de bloqueo almacena el bloqueo sesgado que apunta al hilo actual, no es necesario volver a realizar la operación CAS para bloquear y desbloquear. Cuando otros hilos intentan competir por el bloqueo de polarización, el hilo que sostiene el bloqueo de polarización (no activo) liberará el bloqueo. La optimización de bloqueo de giro no se puede utilizar para bloqueos sesgados, porque una vez que otros subprocesos solicitan bloqueos, la suposición de bloqueos sesgados se destruye y se actualiza a bloqueos ligeros.

Cerraduras ligeras: reducen el consumo de rendimiento de utilizar cerraduras pesadas sin competencia real. La JVM ahora creará un LockRecord de espacio en el marco de pila del hilo actual para almacenar el registro de bloqueo, copiará la palabra de marca en el encabezado del objeto al LockRecord y apuntará el puntero del propietario en el LockRecord al objeto de bloqueo. Luego, el subproceso intentará usar CAS para reemplazar la palabra de marca en el encabezado del objeto con un puntero al registro de bloqueo. Si tiene éxito, el subproceso actual adquiere el bloqueo, y si falla, significa que otros subprocesos compiten por el bloqueo. y el hilo actual intenta adquirir el bloqueo girando. Si la adquisición de giro falla, la cerradura se expande y se actualiza a una cerradura de peso pesado.

Bloqueo pesado: lo realiza el monitor dentro del objeto. La esencia del monitor es la realización del bloqueo Mutex del sistema operativo subyacente. El cambio entre subprocesos del sistema operativo requiere el cambio del modo de usuario al modo de kernel, y el costo de cambio muy alto. La competencia de subprocesos no usa giro y no consume CPU. Pero el hilo entrará en bloqueo esperando ser despertado por otros hilos, y el tiempo de respuesta es lento.

Una imagen de Internet ilustra perfectamente el proceso de actualización de la cerradura sincronizada.

Sincronizado combinado con esperar, notificar, notificar Todo
en el objeto Objeto Java Cuando hablamos antes de sincronizado, encontramos que cuando se despierta el hilo bloqueado, depende de cuándo el hilo que adquirió el bloqueo termina de ejecutar el bloque de código sincronizado y libera el bloqueo . ¿Cómo lograr el control de la pantalla? Necesitamos usar un mecanismo de señal: en el objeto Object, se proporciona esperar / notificar / notificar a todos, que se puede usar para controlar el estado del hilo.

El concepto básico de esperar / notificar / notificar a todos
esperar: indica que el hilo A que mantiene el bloqueo de objeto está listo para liberar la autoridad de bloqueo de objeto, liberar los recursos de la CPU y entrar en el estado de espera.

notificar: indica que el subproceso A que mantiene el bloqueo de objeto está listo para liberar la autoridad de bloqueo de objeto y notifica a la jvm que despierte un subproceso X que compite por el bloqueo de objeto. Después de que finaliza la ejecución sincronizada del código del subproceso A y se libera el bloqueo, el subproceso X obtiene directamente el permiso de bloqueo de objeto, y otros subprocesos competidores continúan esperando (incluso si el subproceso X sincroniza y libera el bloqueo del objeto, otros subprocesos competidores todavía esperan hasta que un nueva notificar, notificar a todo se emite transferencia).

notificar a todos: la diferencia entre notificar a todos y notificar es que notificar a todos despierta todos los subprocesos que compiten por el mismo bloqueo de objeto. Después de que el subproceso A que ha adquirido el bloqueo lo libera, todos los subprocesos despertados pueden obtener el permiso de bloqueo de objeto.

Cabe señalar que los tres métodos deben llamarse en el ámbito definido por la palabra clave sincronizada; de lo contrario, se informará un error java.lang.IllegalMonitorStateException. Esto significa que debido a que no hay sincronización, el subproceso no está seguro del estado del bloqueo del objeto y no se pueden llamar a estos métodos. Además, el mecanismo de sincronización se utiliza para asegurar que el hilo pueda percibir la modificación de la variable realizada por el hilo de notificación cuando regresa del método de espera.

Preguntas comunes en las entrevistas: ¿Por qué esperar / notificar / notificar a todos debe estar sincronizado?

1. La semántica del método de espera tiene dos, una es liberar el bloqueo del objeto actual, la otra es hacer que el hilo actual ingrese a la cola de bloqueo, y estas operaciones están relacionadas con el monitor, por lo que esperar debe obtener un bloqueo del monitor.

2. Lo mismo ocurre con notificar, es para despertar un hilo, ya que quieres despertar, primero debes saber dónde está ?, entonces debes encontrar el objeto para adquirir el bloqueo de este objeto, y luego ir a la cola de espera de este objeto Despierta un hilo.

3. Cada objeto puede tener varios subprocesos que llaman al método de espera, por lo que debe haber una cola de espera para almacenar estos subprocesos bloqueados. Esta cola de espera debe estar vinculada a este objeto. También habrá problemas de seguridad de subprocesos al llamar a métodos de espera y notificación, por lo que se necesita un bloqueo para garantizar la seguridad de subprocesos.

El principio básico de esperar / notificar

————————————————
Declaración de derechos de autor: Este artículo es el artículo original del blogger de CSDN "Song Yue An Ran", y sigue el acuerdo de derechos de autor CC 4.0 BY-SA. Adjunte el enlace de la fuente original y esto para reimprimir.
Enlace original: https://blog.csdn.net/baidu_38083619/article/details/82527461

Supongo que te gusta

Origin blog.csdn.net/zw764987243/article/details/115289610
Recomendado
Clasificación