Red de conocimiento informático - Conocimiento sistemático - Estudio en profundidad de las diferencias y el uso de Java Synchronize and Lock.

Estudio en profundidad de las diferencias y el uso de Java Synchronize and Lock.

En el desarrollo distribuido, los bloqueos son una forma importante de control de subprocesos. Java también proporciona dos mecanismos de bloqueo, sincronizado y bloqueado. Como entusiasta de Java, es natural comparar estos dos mecanismos y también puede aprender algunos puntos a los que se debe prestar atención en el desarrollo distribuido.

Empecemos por el más sencillo y vayamos analizando poco a poco las diferencias entre ambos.

En primer lugar, el uso de sincronizado y bloqueo es diferente.

Sincronizado: agrega el control al objeto que necesita sincronizarse. Sincronizado se puede agregar a métodos o bloques de código específicos, y los objetos que deben bloquearse se indican entre corchetes.

Bloqueo: debe mostrar la posición inicial y final especificadas. Generalmente, la clase ReentrantLock se usa como bloqueo. En subprocesos múltiples, debe haber una clase ReentrantLock como objeto para garantizar que el bloqueo surta efecto. Las posiciones bloqueadas y desbloqueadas deben indicarse mostrando lock() y unlock(). Por lo tanto, unlock() generalmente se escribe en un bloque finalmente para evitar un punto muerto.

La diferencia de uso es relativamente simple, por lo que no entraré en detalles aquí. Si no lo comprende, puede consultar la sintaxis básica de Java.

En segundo lugar, la diferencia de rendimiento entre sincronizado y bloqueado

Sincronizado se confía a la JVM para su ejecución, y el bloqueo es un código escrito en Java para controlar el bloqueo. En Java 1.5, la eficiencia de la sincronización es muy baja. Debido a que se trata de una operación pesada, es necesario llamar a la interfaz de operación, lo que puede hacer que el bloqueo consuma más tiempo del sistema que otras operaciones. En comparación con el uso del objeto de bloqueo proporcionado por Java, el rendimiento es mayor. Pero con Java 1.6, las cosas han cambiado. Synchronize tiene una semántica clara y puede realizar muchas optimizaciones, como giro adaptativo, eliminación de bloqueo, engrosamiento de bloqueo, bloqueos ligeros, bloqueos compensados, etc. Por lo tanto, el rendimiento de sincronización en Java 1.6 no es peor que el de Lock. El funcionario también dijo que también admite sincronización y que hay espacio para optimización en versiones futuras.

Dicho esto, todavía quiero mencionar las diferencias específicas entre los dos mecanismos. Hasta donde yo sé, sincronizado inicialmente utilizó el mecanismo de bloqueo pesimista de la CPU, es decir, el hilo obtiene un bloqueo exclusivo. Un bloqueo exclusivo significa que otros subprocesos solo pueden confiar en el bloqueo para esperar a que el subproceso libere el bloqueo. Sin embargo, cuando el subproceso de conversión de la CPU está bloqueado, se producirá un cambio de contexto de subproceso. Cuando varios subprocesos compiten por los bloqueos, se producirá un cambio frecuente de contexto de la CPU, lo que dará como resultado una baja eficiencia.

El bloqueo utiliza el método de bloqueo optimista. El llamado bloqueo optimista consiste en completar una operación en lugar de bloquearla cada vez, pero suponiendo que no haya conflicto. Si falla debido a un conflicto, se volverá a intentar hasta que tenga éxito. El mecanismo implementado por el bloqueo optimista es la operación CAS (comparar e intercambiar). Podemos estudiar más a fondo el código fuente de ReentrantLock y encontraremos que uno de los métodos más importantes para obtener el bloqueo es compareAndSetState. En realidad, esto es llamar a una instrucción especial proporcionada por la CPU.

Las CPU modernas proporcionan instrucciones que pueden actualizar datos automáticamente y detectar interferencias de otros subprocesos. compareAndSet() las utiliza en lugar de bloqueos. Este algoritmo se denomina algoritmo sin bloqueo, lo que significa que la falla o suspensión de un subproceso no debería afectar la falla o suspensión de otros subprocesos.

Solo conozco este paso. Si los lectores interesados ​​​​están interesados ​​en los algoritmos de la CPU, pueden consultarlo. Si hay una mejor explicación me pueden dejar un mensaje y aprenderé de ello.

En tercer lugar, la diferencia entre sincronización y uso de bloqueo

En términos generales, no hay diferencia entre primitivas sincronizadas y ReentrantLock, pero en aplicaciones de sincronización muy complejas, considere usar ReentrantLock, especialmente cuando cumples con los siguientes dos requisitos.

1. Mientras se espera el control de bloqueo, es necesario interrumpir el hilo.

2. Algunas aplicaciones condicionales deben procesarse en wait-notify y ReentrantLock respectivamente, y el hilo puede controlar la notificación.

3. Con la función de bloqueo justo, cada hilo entrante estará en cola de espera.

El método se presentará en detalle a continuación...

Hablemos primero de la primera situación. ReentrantLock tiene dos mecanismos de bloqueo, ignorando los bloqueos de interrupción y respondiendo a los bloqueos de interrupción, lo que nos brinda una gran flexibilidad. Por ejemplo, si los subprocesos A y B2 compiten por un bloqueo, el subproceso A obtiene el bloqueo y el subproceso B espera, pero el subproceso A realmente tiene demasiadas cosas con las que lidiar en este momento y nunca regresa. El subproceso B puede estar impaciente y querer interrumpir. sí mismo, deja de esperar el bloqueo y pasa a otras cosas. En este momento, ReentrantLock proporciona dos mecanismos. Primero, el subproceso B se interrumpe (u otro subproceso lo interrumpe), pero ReentrantLock no responde, dejando al subproceso B esperando. No importa cómo interrumpas, hago oídos sordos (la primitiva sincronizada es así); en segundo lugar, el hilo B se interrumpe (u otro hilo se interrumpe), ReentrantLock maneja la interrupción y se rinde por completo sin esperar la llegada del bloqueo. (Si no comprende el mecanismo de interrupción de Java, consulte la información relevante y vuelva a leer este artículo. El 80% de las personas no comprende muy bien qué son las interrupciones de Java, jaja).

Estamos haciendo un experimento aquí. Primero, cree una clase Buffer con operaciones de lectura y escritura. Para no leer datos sucios, es necesario bloquear tanto las escrituras como las lecturas. Primero usemos primitivas de sincronización para bloquear de la siguiente manera:

1 búfer de clase pública {

2

3 bloqueos de objetos privados;

cuatro

5 buffer público(){

6 lock=this;

7}

ocho

p>

9 public void write() {

10 sincronización (bloqueo){

11 hora de inicio larga = hora actual en milisegundos();

12 System.out.println("Empieza a escribir datos en este buff...");

13 es (;;) //La simulación lleva mucho tiempo.

14 {

15 if(sistema . hora actual milis()

16-hora de inicio & gt; entero. valor máximo)

17 posición rota;

18 }

19 System.out.println("finalmente completado"); > 21 }

22

23 public void read() {

24 Sincronización (bloqueo){

25 System.out. println ("Leer datos de este beneficio");

26 }

27 }

28