Para usar una analogía: un objeto es como una casa grande, la puerta siempre está abierta. Hay muchas habitaciones (también conocidas como métodos) en una casa. Estas salas están bloqueadas (método sincronizado) o desbloqueadas (método normal). Hay una llave en la puerta de la habitación. Esta llave puede abrir todas las habitaciones cerradas. Además, comparo todos los hilos que quieren llamar a métodos de este objeto con personas que quieren ingresar a una determinada habitación de esta casa. Eso es todo, echemos un vistazo a cómo funcionan estas cosas entre sí.
Aquí primero aclaramos nuestros requisitos previos. El objeto tiene al menos un método sincronizado; de lo contrario, esta clave no tiene significado. Por supuesto, nuestro tema no existirá.
Una persona quiere entrar a una habitación cerrada. Llega a la puerta de la casa y ve la llave allí (lo que indica que nadie más quiere usar la habitación cerrada todavía). Así que subió, cogió las llaves y utilizó las habitaciones como había planeado. Tenga en cuenta que devuelve la llave inmediatamente después de cada uso de la habitación cerrada. Incluso si quiere usar dos habitaciones cerradas seguidas, tiene que devolver la llave y recuperarla en el medio.
Por lo tanto, el principio de uso de claves en circunstancias normales es: "Pídalas prestadas cuando las use y devuélvalas tan pronto como las use".
En este momento, otras personas pueden usar las habitaciones desbloqueadas sin restricciones. Una persona puede usar una habitación o dos personas pueden usar una habitación. No hay restricciones. Pero si alguien quiere entrar en una habitación cerrada, tiene que correr hacia la puerta y mirar. Si tienes la llave, por supuesto que puedes tomarla e irte. Si no tienes la llave, solo puedes esperar.
Si hay muchas personas esperando la llave, ¿quién la recibirá primero cuando se la devuelva? No garantizado. Como el tipo en el ejemplo anterior que quiere usar dos habitaciones cerradas seguidas, si hay otras personas esperando la llave cuando él la devuelve, no hay garantía de que este tipo pueda recuperarla nuevamente. (La especificación JAVA establece claramente en muchos lugares que no hay garantías, como cuánto tiempo le tomará a Thread.sleep() volver a ejecutarse después de un descanso, qué subproceso con la misma prioridad se ejecutará primero y cuándo Se libera el bloqueo para acceder al objeto y varios subprocesos en el grupo de espera lo obtendrán primero. Etc. Creo que la decisión final recae en la JVM. La razón por la que no hay garantía es porque cuando la JVM toma la decisión anterior, no emite un juicio basándose simplemente en una condición, sino en muchas condiciones. muchas condiciones de juicio, si las dice, puede afectar a JAVA. La promoción puede deberse a la protección de la propiedad intelectual. SUN no dio ninguna garantía y es comprensible, pero creo que estas incertidumbres no son completamente inciertas porque la computadora en sí funciona según las instrucciones. Hay reglas que encontrar. Cualquiera que haya estudiado computadoras sabe que el nombre científico de los números aleatorios en las computadoras es números pseudoaleatorios, que son escritos por personas usando un determinado método. Además, puede ser porque ellos también quieren hacerlo. Seguro que es un gran problema y no tiene mucho sentido, así que si no estás seguro, no lo estés.)
Veamos nuevamente los bloques de código sincronizados. Es ligeramente diferente del método de sincronización.
1. En términos de tamaño, los bloques de código sincronizados son más pequeños que los métodos sincronizados. Puedes pensar en un bloque de código sincronizado como un espacio en una habitación desbloqueada separada por una pantalla bloqueada.
2. El bloque de código de sincronización también puede especificar manualmente la clave de otro objeto. Es como especificar qué llave se puede usar para abrir el bloqueo de esta pantalla. Puedes usar la llave de esta casa; también puedes especificar la llave de otra casa para abrirla. para abrir la cerradura. Obtenga esa llave y use esa llave de la casa para abrir la pantalla bloqueada de esta casa.
Recuerde que la llave de otra casa que obtuvo no impide que otros entren a las habitaciones desbloqueadas de esa casa.
¿Por qué utilizar bloques de código sincronizados? Creo que debería ser así: en primer lugar, la parte de sincronización del programa afecta la eficiencia operativa, y un método generalmente crea primero algunas variables locales y luego realiza algunas operaciones en estas variables, como cálculos, visualización, etc. ; y la sincronización cubre Cuanto más código haya, más grave será el impacto en la eficiencia. Por eso normalmente intentamos mantener su alcance de impacto lo más pequeño posible. ¿Cómo hacerlo? Bloques de código sincronizados. Solo sincronizamos las partes de un método que deben sincronizarse, como las operaciones.
Además, el bloque de código de sincronización puede especificar la clave. Esta característica tiene el beneficio adicional de ocupar la clave de un objeto dentro de un cierto período de tiempo. Recuerde lo que dije antes sobre los principios del uso de claves en circunstancias normales. Éstas no son circunstancias ordinarias. La clave que obtiene no se devuelve para siempre, sino que solo se devuelve cuando sale del bloque de código sincronizado.
Usemos la analogía del tipo de antes que quería usar dos habitaciones cerradas seguidas. ¿Cómo puedo seguir usando otra habitación después de usar una habitación? Utilice bloques de código sincronizados. Primero cree otro hilo, cree un bloque de código de sincronización y apunte el candado de ese bloque de código a la llave de la casa. Entonces inicia ese hilo. Siempre que puedas tomar la llave de la casa cuando ingresas a ese bloque de código, puedes conservarla hasta que salgas de ese bloque de código. En otras palabras, incluso puedes atravesar todas las habitaciones cerradas en esta habitación e incluso dormir (10 * 60 * 1000), pero todavía hay 1000 hilos esperando la llave en la puerta. Es bastante divertido.
Hablemos de la correlación entre el método sleep() y la clave. Si un hilo se ve obligado a dormir() después de obtener la clave y no ha completado el contenido de sincronización, la clave seguirá allí. La clave no se devolverá hasta que se ejecute nuevamente y se complete todo el contenido de la sincronización. Recuerde, el tipo simplemente se cansó del trabajo y se fue a descansar. No terminó lo que quería hacer. Para evitar que otros entraran a la habitación y ensuciaran el interior, tenía que usar la única llave en su cuerpo incluso cuando dormía.
Finalmente, alguien podría preguntarse, ¿por qué necesitamos una llave para abrir cada puerta, en lugar de una llave para cada puerta? Creo que esto es puramente una cuestión de complejidad. Una llave para una puerta es ciertamente más segura, pero traerá muchos problemas. La generación, almacenamiento, adquisición, devolución de claves, etc. Su complejidad puede aumentar geométricamente a medida que aumenta el número de métodos de sincronización, afectando seriamente la eficiencia. Esto puede considerarse como una cuestión de compensación. Qué indeseable es reducir mucho la eficiencia para aumentar un poco la seguridad.