Palabra clave sincronizada
La clave sincronizada puede modificar funciones y declaraciones dentro de las funciones. Ya sea que se agregue a los métodos u objetos, el bloqueo que adquiere es un objeto, en lugar de tratar un código o función como bloqueo.
1. Cuando dos hilos concurrentes acceden al bloque de código sincronizado (este) en el mismo objeto, solo se puede ejecutar un hilo por un período de tiempo, y el otro hilo solo puede ejecutar este código después de que el hilo actual haya completado la ejecución.
2. Cuando un hilo accede a un bloque de código sincronizado (este) sincronizado en un objeto, otros subprocesos aún pueden acceder a otros bloqueos de código no sincronizados (este) en este objeto.
3. Cabe señalar aquí que cuando un subproceso accede a un bloque de código sincronizado (este) de un objeto, se bloqueará otros hilos para acceder a otros bloques de código sincronizados (esto) en este objeto.
4. Lo anterior también es aplicable a otros bloques de código de sincronización, es decir, cuando un subproceso accede a un bloque de código de sincronización sincronizado (este) de un objeto, el hilo obtiene el bloqueo del objeto del objeto. Además, cada objeto (es decir, instancia de clase) corresponde a un bloqueo. pertenece está bloqueado. Este mecanismo asegura que al mismo tiempo, para cada objeto, como máximo una de todas las funciones de los miembros declaradas sincronizadas, se encuentre en un estado ejecutable (porque a lo sumo un hilo puede adquirir el bloqueo del objeto), evitando así el acceso a las variables de los miembros de la clase en conflicto .
Desventajas del método sincronizado:
Dado que sincronizado bloquea el objeto que llama a este método de sincronización, es decir, cuando un hilo P1 ejecuta este método en diferentes hilos, formarán exclusiones mutuas, logrando así el efecto de sincronización. Pero debe tenerse en cuenta aquí que otro objeto de clase que es de este objeto puede llamar arbitrariamente a este método con la palabra clave sincronizada agregada. La esencia del método de sincronización es aplicar sincronizado en la referencia del objeto. Este caso. Explicaremos esta situación en detalle a continuación:
Primero, introduzcamos dos objetos bloqueados con la palabra clave sincronizada: objeto y clase: sincronizado puede agregar bloqueos de objetos o bloqueos de clase a los recursos. De esta clase, otros objetos de esta clase aún pueden usar el método sincronizado que ha bloqueado el objeto anterior.
Uno de los principales problemas que discutimos aquí es: "¿La misma clase y diferentes instancias llamarán el mismo método, ¿habrá un problema de sincronización?"
El problema de sincronización solo está relacionado con el recurso, y depende de si el recurso es estático. Para los mismos datos estáticos, su misma función pertenece a diferentes hilos para leerlos y escribirlo al mismo tiempo, y la CPU no generará errores. Tú. Incluso si tiene dos códigos diferentes que se ejecutan en dos núcleos diferentes de la CPU y escriben una dirección de memoria al mismo tiempo, el mecanismo de caché bloqueará uno en L2 primero. Luego actualice y compártalo con otro núcleo, y no habrá errores, de lo contrario Intel o AMD serán en vano.
Por lo tanto, siempre que no tenga el mismo recurso o variable que dos acciones de código, no habrá inconsistencia de datos. Además, las llamadas a diferentes objetos de la misma clase tienen pilas completamente diferentes, y son completamente irrelevantes.
Aquí utilizamos un ejemplo para ilustrar el proceso de venta de entradas, donde nuestros recursos compartidos son el número restante de boletos.
paquete com.test; public class ThreadSafeTest Extiende Thread Implements Runnable {private static int vender void (nombre de cadena) {if (num> 0) {System. (completado en aproximadamente 5 segundos). ; ("Sistema: Número actual de votos:" + num); Args []) {try {New ThreadSafetest ("Vendedor de boletos Li XX") .Start (); {E.PrintStackTrace (); Ejecute el código anterior y la salida que obtenemos es:
Director de boletos Li XX: El número de boletos de prueba es mayor que 0 Ticket Conductor Li XX: El pago se está recaudando (completado en aproximadamente 5 segundos). . . Vendedor de boletos King X: El número de boletos de prueba es mayor que las 0 del vendedor de boletos King X: el pago se está recaudando (completado en aproximadamente 5 segundos). . . Vendedor de boletos Li XX: Imprima el proyecto de ley, Sistema de finalización de ventas de boletos: Número actual de votos: 0 Vendedor de boletos Wang X: Imprima el proyecto de ley, Sistema de finalización de ventas de boletos: Número actual de votos: -1 ADVERTENCIA: El número de votos es menor que el 0, aparecen números negativos
Según los resultados de la salida, podemos encontrar que los votos restantes son -1, y hay un problema de error de sincronización. La razón de esto es que los dos objetos de instancia que creamos han modificado el recurso estático compartido static int num = 1 al mismo tiempo. Luego eliminamos el modificador estático en el cuadro en el código anterior y luego ejecutamos el programa para obtener:
Director de boletos Li XX: El número de boletos de prueba es mayor que 0 Ticket Conductor Li XX: El pago se está recaudando (completado en aproximadamente 5 segundos). . . Vendedor de boletos King X: El número de boletos de prueba es mayor que las 0 del vendedor de boletos King X: el pago se está recaudando (completado en aproximadamente 5 segundos). . . Vendedor de boletos Li XX: Imprima la factura, Sistema de finalización de ventas de boletos: Número actual de boletos: 0 Vendedor de boletos Wang X: Imprima la factura, Sistema de finalización de ventas de boletos: Número actual de boletos: 0
Después de modificar el título, el programa se ejecuta sin ningún problema. Pero esto va en contra de nuestra expectativa de que múltiples hilos pueden manejar recursos compartidos al mismo tiempo (después de la estática, los cambios de NUM de los recursos compartidos a las variables miembros propiedad de cada instancia), que obviamente no es lo que queremos.
En los dos códigos anteriores, lo principal que debe adoptar es bloquear el objeto. Por la razón que mencioné anteriormente, cuando dos instancias diferentes de una clase modifican el mismo recurso compartido, la CPU predeterminará la lógica del programa. Por lo tanto, necesitamos cambiar el alcance del bloqueo. al mismo tiempo.
paquete com.test; public class ThreadSafeTest Extiende Thread Implements Runnable {private static int num = 1; static vender (nombre de la cadena) {if (num> 0) {System. recolectando el pago (aproximadamente 5 segundos). (); .println ("Sistema: Recuento de voto actual:" + num); String args []) {try {New ThreadSafetest ("Vendedor de boletos Li XX") .Start (); ) {E.PrintStackTrace (); Haga el programa como se indicó anteriormente para obtener el resultado de ejecución:
Director de boletos Li XX: El número de boletos de prueba es mayor que 0 Ticket Conductor Li XX: El pago se está recaudando (completado en aproximadamente 5 segundos). . . Vendedor de boletos Li XX: Imprima el boleto, Sistema de finalización de ventas de boletos: Número actual de boletos: 0 Vendedor de boletos Wang X: Sin boletos, Stop Boleting Sales
Se agrega un modificador estático al método sell (), de modo que el objeto del bloqueo se convierte en una clase. Esto obtendrá los resultados que queremos como se esperaba.
Resumir:
1. Hay dos usos de palabras clave sincronizadas: método sincronizado y bloque sincronizado.
2. En Java, no es solo una instancia de clase, sino que cada clase también puede corresponder a un bloqueo.
1. La palabra clave sincronizada no se puede heredar. Aunque el sincronizado se puede utilizar para definir métodos, Synchronized no pertenece a una parte de la definición del método, por lo que la palabra clave sincronizada no puede ser heredada. Si un método en la clase principal usa la palabra clave sincronizada y la subclase también anula este método, de manera predeterminada, este método en la subclase no se ha sincronizado, y debe mostrarse para agregar el método en la subclase. . Por supuesto, también puede llamar a los métodos correspondientes en la clase principal en la subclase. siendo sincronizado. como,
Agregue la palabra clave sincronizada a la subclase:
Class Parent {Public Synchronized void Method () {}} Class Child extiende el padre {Método void sincronizado público () {}} Llame al método de clase principal:
Class Parent {Public Synchronized Void Method () {}} Class Child extiende el padre {Public void Method () {super.method (); 2. La palabra clave sincronizada no se puede usar al definir el método de interfaz.
3. El constructor no puede usar la palabra clave sincronizada, pero el bloque sincronizado se puede usar para la sincronización.
4. La posición sincronizada se puede colocar libremente, pero no se puede colocar detrás del tipo de retorno del método.
5. La palabra clave sincronizada no se puede usar para sincronizar variables, como el siguiente código es incorrecto:
público sincronizado int n = 0;
6. Aunque el uso de la palabra clave sincronizada es el método de sincronización más seguro, si se usa en grandes cantidades, también causará pérdidas innecesarias de consumo de recursos y rendimiento. En la superficie, sincronizado bloquea un método, pero de hecho bloquea una clase. ejecutado. Los métodos estáticos son similares a los métodos no estáticos. Sin embargo, los métodos estáticos y los métodos no estatales no se afectarán entre sí, consulte el siguiente código:
Public Class MyThread1 extiende el hilo {Methodname de cadena pública; ");} public sincronizado Void Method2 () {método (" Método no estático2 "Método");} public static static sincronizado Void Method3 () {Method ("Método estático3 del método");} Public Static sincronizado Void Method4 () {Method ("Método Static4"); ) lanza la excepción {MyThread1 myThread1 = new MyThread1 (); ); El resultado de la ejecución es:
Método no estático1 método static method3 método
De los resultados de ejecución anteriores, podemos ver que el método2 y el método4 no se ejecutarán hasta que se completen el método1 y el método3. Por lo tanto, podemos sacar una conclusión de que si usamos sincronizados para definir métodos no estáticos en la clase, afectará todos los métodos no estáticos definidos sincronizados en esta clase; En esta clase, método estático definido por sincronizado. Esto es un poco como un bloqueo de tabla en una tabla de datos. Por lo tanto, el uso intensivo de este método de sincronización reducirá en gran medida el rendimiento del programa.
Consejos para un acceso más seguro a recursos compartidos:
1. Defina la variable de instancia de privado + su método get, en lugar de definir la variable de instancia de pública/protegida. Si una variable se define como pública, el objeto puede obtenerlo directamente omitir el control del método de sincronización en el mundo exterior y cambiarlo. Esta es también una de las implementaciones estándar de Javabean.
2. Si la variable de instancia es un objeto, como una matriz o una lista de matrices, entonces el método anterior aún no es seguro, porque cuando el mundo exterior obtiene la referencia al objeto de instancia a través del método Get y lo señala a otro objeto, entonces entonces La variable privada también cambió, ¿no sería muy peligroso? En este momento, debe agregar sincronizado para obtener el método y solo devolver clone () de este objeto privado. De esta manera, lo que la persona llama es solo una referencia a la copia del objeto.
Tres formas de obtener el monitor de objeto (bloqueo) y notificar ()
En un método de subprocesamiento, las llamadas a esperar () y notificar () debe especificar un objeto de objeto, y el subproceso debe tener el monitor del objeto objeto. La forma más fácil de obtener el monitor de objeto es usar la palabra clave sincronizada en el objeto. Después de llamar al método Wait (), el hilo liberará el bloqueo del objeto e ingresará el estado de sueño. Cuando otros hilos llaman al método notify (), se debe usar el mismo objeto de objeto.
Para múltiples métodos bloqueados por un objeto, uno de ellos se seleccionará para despertarse cuando llame al método notify (), y notifyall () despertará todos sus hilos de espera.
paquete net.mindview.util; import javax.swing.jframe; clase pública WaitandNotify {public static void main (string [] args) {System out.println ("¡Hola World!"); frame.setDefaultCleOperation (jFrame. Exit_on_close); 300, 100); {t = New WaitandNotifyThread (WaitandNotifyJFrame.THIS); .add (inicio); (pausa); .add (final); = f; if (isWait) Wait (); Como en el código en el cuadro de ejemplo anterior, si se elimina el bloque de código sincrónico, la ejecución lanzará una excepción java.lang.illegalmonitorStateException.
Mirando el JDK, podemos ver que la razón de esta excepción es que el hilo actual no es el propietario de este monitor de objeto.
Este método solo debe ser llamado por un hilo que es el propietario de este monitor de objeto.
1. Al ejecutar el método de instancia sincrónica de este objeto, como:
public sincronizado n () {notify (); 2. Ejecutando el cuerpo de la declaración sincronizada que se sincroniza en este objeto, como:
public void n () {sincronizado (this) {notify (); 3. Para los objetos de tipo de clase, puede ejecutar métodos estáticos síncronos de esta clase.
Al llamar a un método estático, no necesariamente creamos un objeto de instancia. Por lo tanto, esto no puede usarse para sincronizar los métodos estáticos, por lo que el objeto de clase debe usarse para sincronizar los métodos estáticos. Otro ejemplo para ilustrar:
clase pública SincronizedStatic implementos runnables {private static static boolean bander = true; // Método de sincronización de objetos de clase uno: // Presta atención al método de sincronización de modificación estática, monitor: sincronizado i = 0; // Método de sincronización de objetos de clase 2: Void private testSyncblock () {// Display usa la clase Obtener como monitor. Es lo mismo que el método sincronizado estático consigue implícitamente el monitor de clase. Synchronized (SynchronizedStatic. ("testSyncblock:" + i); Por lo tanto, diferentes hilos ejecutarán diferentes métodos, y solo de esta manera puede ver diferentes efectos de bloqueo. if (flag) {flag = false; = SynChronizedStatic (); El código anterior ejecuta el resultado de la ejecución de dos métodos de sincronización para imprimir 100 números de 0 a 99 al mismo tiempo. Bloque. Es una clase. Los dos métodos son similares. Dado que el alcance del método uno y el método dos son ambas clases, son mutuamente excluyentes. Por lo tanto, el resultado en ejecución del programa será:
testSyncmethod: 0Testsyncmethod: 1 ... ... testSyncmethod: 99Testsyncblock: 0 ... ... testSyncblock: 99
Sin embargo, si reemplazamos SynCronizedStatic.
testSyncblock: 0TestSyncmethod: 0TestSyncblock: 1TestSyncmethod: 1 ... ... testSyncmethod: 99TestSyncblock: 99
Hay dos ámbitos de cerraduras, uno es el objeto de la clase y el otro es la clase misma. En el código anterior, se dan dos métodos para hacer el alcance del bloqueo de una clase, de modo que la sincronización se puede completar entre diferentes objetos de la misma clase.
Para resumir los anteriores, deben tenerse en cuenta los siguientes puntos:
1. Wait (), notify () y notifyall () Todos deben ejecutarse bajo la premisa de tener el monitor de objeto, de lo contrario se lanzará un java.lang.illegalmonitorStateException.
2. Múltiples hilos pueden esperar en un objeto al mismo tiempo.
3. Notificar () es despertar aleatoriamente un hilo esperando el objeto.
4. El hilo que se despierta por notificar () no se despierta inmediatamente después de que se ejecuta notificar (), pero solo después de que el hilo notify () libera el monitor del objeto.
5. Estos métodos de objeto aún están lejos de los métodos de sueño e interrupción de hilos, así que no los confundas.