Cuando se usa una palabra clave de lenguaje Java para modificar un método o un bloque de código, puede asegurarse de que, como máximo, un subproceso ejecuta el código al mismo tiempo.
1. Cuando dos hilos concurrentes acceden a este bloque de código sincronizado (este) sincronizado en el mismo objeto de objeto, solo se puede ejecutar un hilo dentro de una vez. Otro hilo debe esperar a que el hilo actual ejecute este bloque de código antes de que pueda ejecutar el bloque de código.
2. Sin embargo, cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, otro hilo aún puede acceder al bloque de código de sincronización no sincronizado (esto) en ese objeto.
3. Es particularmente crítico que cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, otros subprocesos bloquearán el acceso a todos los otros bloques de código de sincronización sincronizados (esto) en el objeto.
4. El tercer ejemplo también se aplica a otros bloques de código sincrónicos. Es decir, cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, obtiene el bloqueo del objeto de este objeto. Como resultado, otros subprocesos acceden a todas las partes de código sincrónico del objeto del objeto se bloquea temporalmente.
5. Las reglas anteriores también se aplican a otros bloqueos de objetos.
Dar un ejemplo:
1. Cuando dos hilos concurrentes acceden a este bloque de código sincronizado (este) sincronizado en el mismo objeto de objeto, solo se puede ejecutar un hilo dentro de una vez. Otro hilo debe esperar a que el hilo actual ejecute este bloque de código antes de que pueda ejecutar el bloque de código.
paquete ths; public class Thread1 implementa Runnable {public void run () {sincronizado (this) {for (int i = 0; i <5; i ++) {system.out.println (thread.currentThread (). getName () + "bucle sincronizado" + i); }}} public static void main (string [] args) {thread1 t1 = new Thread1 (); Hilo ta = nuevo hilo (t1, "a"); Hilo tb = nuevo hilo (t1, "b"); ta.Start (); tb.start (); }}resultado:
Un bucle sincronizado 0
Un bucle sincronizado 1
Un bucle sincronizado 2
Un bucle 3 sincronizado 3
Un bucle sincronizado 4
B bucle sincronizado 0
B bucle sincronizado 1
B bucle sincronizado 2
B bucle sincronizado 3
B bucle sincronizado 4
2. Sin embargo, cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, otro hilo aún puede acceder al bloque de código de sincronización no sincronizado (esto) en ese objeto.
paquete ths; public class Thread2 {public void m4t1 () {sincronizado (this) {int i = 5; while (i--> 0) {System.out.println (Thread.CurrentThread (). getName () + ":" + i); intente {thread.sleep (500); } capt (interruptedException IE) {}}}} public void m4t2 () {int i = 5; while (i--> 0) {System.out.println (Thread.CurrentThread (). getName () + ":" + i); intente {thread.sleep (500); } capt (interruptedException IE) {}}} public static void main (string [] args) {final hilo2 myt2 = new Thread2 (); Thread t1 = new Thread (new runnable () {public void run () {myt2.m4t1 ();}}, "t1"); Thread t2 = new Thread (new runnable () {public void run () {myt2.m4t2 ();}}, "t2"); t1.start (); t2.start (); }} resultado:
T1: 4
T2: 4
T1: 3
T2: 3
T1: 2
T2: 2
T1: 1
T2: 1
T1: 0
T2: 0
3. Es particularmente crítico que cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, otros subprocesos bloquearán el acceso a todos los otros bloques de código de sincronización sincronizados (esto) en el objeto.
// Modificar el método Thread2.m4t2 (): public void m4t2 () {sincronizado (this) {int i = 5; while (i--> 0) {System.out.println (Thread.CurrentThread (). getName () + ":" + i); intente {thread.sleep (500); } capt (interruptedException es decir) {}}}}resultado:
T1: 4
T1: 3
T1: 2
T1: 1
T1: 0
T2: 4
T2: 3
T2: 2
T2: 1
T2: 0
4. El tercer ejemplo también se aplica a otros bloques de código sincrónicos. Es decir, cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, obtiene el bloqueo del objeto de este objeto. Como resultado, otros subprocesos acceden a todas las partes de código sincrónico del objeto del objeto se bloquea temporalmente.
// modifica el método thread2.m4t2 () de la siguiente manera: public sincronizado void m4t2 () {int i = 5; while (i--> 0) {System.out.println (Thread.CurrentThread (). getName () + ":" + i); intente {thread.sleep (500); } capt (interruptedException IE) {}}} resultado:
T1: 4
T1: 3
T1: 2
T1: 1
T1: 0
T2: 4
T2: 3
T2: 2
T2: 1
T2: 0
5. Las reglas anteriores también se aplican a otros bloqueos de objetos:
paquete ths; public class Thread3 {class Inner {private void m4t1 () {int i = 5; while (i--> 0) {System.out.println (Thread.CurrentThread (). getName () + ": inner.m4t1 () =" + i); intente {thread.sleep (500); } capt (interruptedException ie) {}}} private void m4t2 () {int i = 5; while (i--> 0) {System.out.println (Thread.CurrentThread (). getName () + ": inner.m4t2 () =" + i); intente {thread.sleep (500); } capt (interruptedException IE) {}}}} private void m4t1 (Inner Inner) {SynChronized (Inner) {// Use Object Lock Inner.m4t1 (); } private void m4t2 (interno interno) {inner.m4t2 (); } public static void main (string [] args) {final thread3 myt3 = new Thread3 (); final interno interno = myt3.new inner (); Thread t1 = new Thread (new runnable () {public void run () {myt3.m4t1 (interno);}}, "t1"); Thread t2 = new Thread (new runnable () {public void run () {myt3.m4t2 (interno);}}, "t2"); t1.start (); t2.start (); }}resultado:
Aunque el hilo T1 obtiene un bloqueo de objeto en el interno, ya que el hilo T2 accede a la parte asincrónica en el mismo interno. Por lo tanto, los dos hilos no interfieren entre sí.
T1: inner.m4t1 () = 4
t2: inner.m4t2 () = 4
T1: inner.m4t1 () = 3
t2: inner.m4t2 () = 3
T1: inner.m4t1 () = 2
t2: inner.m4t2 () = 2
T1: inner.m4t1 () = 1
t2: inner.m4t2 () = 1
t1: inner.m4t1 () = 0
t2: inner.m4t2 () = 0
Ahora ponte sincronizado frente a Inner.m4t2 ()::
Void sincronizado privado m4t2 () {int i = 5; while (i--> 0) {System.out.println (Thread.CurrentThread (). getName () + ": inner.m4t2 () =" + i); intente {thread.sleep (500); } capt (interruptedException IE) {}}}resultado:
Aunque los hilos T1 y T2 acceden a dos partes no relacionadas del mismo objeto interno, porque T1 obtiene primero el bloqueo del objeto al interior, el acceso de T2 a Inner.m4t2 () también está bloqueado porque M4T2 () es un método de sincronización en el interior.
T1: inner.m4t1 () = 4
T1: inner.m4t1 () = 3
T1: inner.m4t1 () = 2
T1: inner.m4t1 () = 1
t1: inner.m4t1 () = 0
t2: inner.m4t2 () = 4
t2: inner.m4t2 () = 3
t2: inner.m4t2 () = 2
t2: inner.m4t2 () = 1
t2: inner.m4t2 () = 0
Artículo 2:
Palabra clave sincronizada, que incluye dos usos: el método sincronizado y el bloque sincronizado.
1. Método sincronizado: declare el método sincronizado agregando la palabra clave sincronizada a la declaración del método. como:
Public sincronizado accessval (int newval);
El método sincronizado controla el acceso a las variables de miembros de la clase: cada instancia de clase corresponde a un bloqueo, y cada método sincronizado debe obtener el bloqueo de la instancia de clase que llama al método antes de que pueda ejecutarse. De lo contrario, el hilo al que pertenece está bloqueado. Una vez que se ejecuta el método, ocupará exclusivamente el bloqueo. El bloqueo no se lanzará hasta que regrese del método. El hilo bloqueado puede obtener el bloqueo y volver a ingresar el estado ejecutable. Este mecanismo asegura que al mismo tiempo, para cada instancia de clase, como máximo una de las funciones de los miembros declaradas sincronizadas, se encuentre en un estado ejecutable (porque a lo sumo uno puede obtener el bloqueo correspondiente a la instancia de clase), evitando así efectivamente los conflictos de acceso de las variables de los miembros de la clase (siempre que todos los métodos posibles para acceder a las variables de los miembros de la clase se declaren sincronizados).
En Java, no solo instancias de clase, sino que cada clase también corresponde a un bloqueo, por lo que podemos declarar que la función de miembro estático de la clase como sincronizado para controlar su acceso a las variables de miembros estáticos de la clase.
La desventaja del método sincronizado: declarar un método grande como sincronizado afectará en gran medida la eficiencia. Por lo general, si el método de la clase de hilo () se declara como sincronizado, ya que se ha ejecutado a lo largo de la vida del hilo, hará que nunca tenga éxito en ningún método sincronizado de esta clase. Por supuesto, podemos resolver este problema colocando el código que accede a las variables de los miembros de la clase en un método especial, declarándolo como sincronizado y llamándolo en el método principal, pero Java nos proporciona una mejor solución, es decir, el bloque sincronizado.
2. Bloque sincronizado: declare el bloque sincronizado a través de la palabra clave sincronizada. La sintaxis es la siguiente:
sincronizado (syncObject) {// código que permite el control de acceso} El bloque sincronizado es un bloque de código en el que el código debe obtener un bloqueo del syncObject de objeto (como se mencionó anteriormente, puede ser una instancia o clase de clase) antes de que pueda ejecutarse. El mecanismo específico es el mismo que el descrito anteriormente. Dado que se puede dirigir en cualquier bloque de código y los objetos bloqueados se pueden especificar en cualquier momento, es más flexible.
Algunas entendimientos de sincronizado (esto) <Br /> 1. Cuando dos hilos concurrentes acceden a este bloque de código sincronizado (este) sincronizado en el mismo objeto, solo se puede ejecutar un hilo dentro de una vez. Otro hilo debe esperar a que el hilo actual ejecute este bloque de código antes de que pueda ejecutar el bloque de código.
2. Sin embargo, cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, otro hilo aún puede acceder al bloque de código de sincronización no sincronizado (esto) en ese objeto.
3. Es particularmente crítico que cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, otros subprocesos bloquearán el acceso a todos los otros bloques de código de sincronización sincronizados (esto) en el objeto.
4. El tercer ejemplo también se aplica a otros bloques de código sincrónicos. Es decir, cuando un hilo accede a un bloque de código de sincronización sincronizado (este) de un objeto, obtiene el bloqueo del objeto de este objeto. Como resultado, otros subprocesos acceden a todas las partes de código sincrónico del objeto del objeto se bloquea temporalmente.
5. Las reglas anteriores también se aplican a otros bloqueos de objetos.
Cómo usar sincronizado en Java
Por ejemplo: un objeto es como una casa grande, la puerta siempre está abierta. Hay muchas habitaciones en la casa (es decir, el método).
Estas habitaciones han bloqueado (método sincronizado) y no están bloqueadas (método normal). Hay una llave en la puerta, que puede abrir todas las habitaciones cerradas.
Además, comparo todos los hilos que quieren llamar al método del objeto a las personas que desean ingresar a una habitación en esta casa. Solo hay tantas cosas, veamos cómo funcionan estas cosas.
Aquí primero aclaramos nuestros requisitos previos. El objeto tiene al menos un método sincronizado, de lo contrario, ¿cuál es el punto de esta clave? Por supuesto, no habrá tal tema para nosotros.
Un hombre quería entrar en una habitación cerrada. Llegó a la puerta de la casa y vio la llave allí (significa que nadie más quiere usar la habitación cerrada todavía). Entonces se acercó y consiguió las llaves y usó las habitaciones mientras planeaba. Tenga en cuenta que devolverá la llave inmediatamente después de usar la habitación cerrada cada vez. Incluso si quiere usar dos habitaciones cerradas seguidas, devolverá las llaves para recuperarlas. Por lo tanto, el principio de usar una clave en casos ordinarios es: "Pedir prestado como usa y devuélvalo tan pronto como lo use".
En este momento, otras personas pueden usar esas habitaciones desbloqueadas sin restricciones. Una persona puede usar una habitación, y dos personas pueden usar una habitación, sin restricciones. Pero si alguien quiere entrar en una habitación cerrada, tiene que correr hacia la puerta para echar un vistazo. Por supuesto, si tienes la llave, te irás. Si no lo tienes, solo puedes esperar. Si muchas personas están esperando esta clave, ¿quién obtendrá la clave primero después de que se devuelva? No garantizado. Al igual que el tipo en el ejemplo anterior que quería usar dos habitaciones cerradas seguidas, si hubiera otras personas esperando las llaves en el medio, no había garantía de que este tipo lo recuperaría nuevamente. (La especificación de Java establece claramente que no está garantizada en muchos lugares, como cuánto tiempo tarda en thread.sleep () volver a ejecutarse después del descanso, el hilo con la misma prioridad se ejecuta primero, y qué hilo en el grupo de espera se dará prioridad después de que el bloqueo para acceder al objeto se publique, etc. Creo que la decisión final es solo en el JVM. condición, pero basada en muchos artículos.
Debido a que hay demasiadas condiciones de juicio, si lo dice, puede afectar la promoción de Java, o puede deberse a la protección de la propiedad intelectual. Sun me dio una promesa y la superó. No hay nada de malo en eso. Pero creo que estas incertidumbres no son del todo inciertas. Porque la computadora en sí se ejecuta según las instrucciones. Incluso si el fenómeno parece aleatorio, en realidad es regular. Cualquiera que haya estudiado computadoras sabe que el nombre científico de los números aleatorios en las computadoras son los números pseudo-aleatorios, que son escritos por personas que usan ciertos métodos, y simplemente se ven aleatorios. Además, tal vez sea porque es demasiado difícil asegurarse y no muy significativo, por lo que si no está seguro, no está seguro. )
Echemos un vistazo al bloque de código de sincronización. Hay una ligera diferencia con el método de sincronización.
1. En términos de tamaño, el bloque de código de sincronización es más pequeño que el método de sincronización. Puede pensar en el bloque de código de sincronización 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 artificialmente la clave de obtener un cierto otro objeto. Al igual que especificar qué tecla desbloquear la pantalla, puede usar la tecla de esta habitación; También puede especificar que la clave de otra casa puede abrirla. De esta manera, debe correr a otra casa para traer esa llave y usar la llave de esa casa para abrir la pantalla bloqueada de esta casa.
Recuerde que la clave de esa otra casa que ha obtenido no afecta a otras personas que ingresan a la habitación sin cerraduras en esa casa.
¿Por qué usar bloques de código sincrónicos? Creo que debería ser así: en primer lugar, la parte de sincronización del programa tiene mucho impacto en la eficiencia de la operación, y un método generalmente es crear algunas variables locales primero, y luego hacer algunas operaciones en estas variables, como operaciones, pantalla, etc.; y cuanto más código cubierto por la sincronización, más grave es el impacto en la eficiencia. Por lo tanto, generalmente tratamos de reducir su impacto.
¿Cómo hacerlo? Sincronizar bloques de código. Solo sincronizamos los lugares de sincronización en un método, como las operaciones.
Además, la característica de los bloques de código sincrónicos que pueden especificar las claves tiene una ventaja adicional, que es que puede ocupar las claves de un objeto dentro de un cierto período de tiempo. ¿Recuerdas los principios de usar claves en situaciones ordinarias mencionadas anteriormente? No es la situación ordinaria ahora. La clave que obtuvo nunca se devuelve, sino que solo se devuelve cuando sale del bloque de código sincrónico.
También usé al chico delante que quería usar dos habitaciones cerradas seguidas para dar un ejemplo. ¿Cómo puedo seguir usando otro después de usarlo? Use bloques de código sincrónicos. Primero cree otro hilo, haga un bloque de código sincrónico y apunte el bloqueo de ese bloque de código a la llave de la casa. Luego comience ese hilo. Siempre que pueda obtener la llave de la casa al ingresar ese bloque de código, puede mantenerla hasta que salga de ese bloque de código. En otras palabras, incluso puede atravesar todas las habitaciones cerradas en esta habitación, o incluso dormir (10*60*1000), y todavía hay 1,000 hilos esperando esta llave en la puerta. Muy agradable.
Aquí hablaremos sobre la correlación entre el método Sleep () y la clave. Si un hilo se ve obligado a dormir () después de obtener la llave y no ha completado el contenido sincrónico, la clave aún está allí. La clave no se devolverá hasta que se ejecute nuevamente y completa todo el contenido sincrónico. Recuerde, ese tipo estaba cansado de trabajar, así que fue a tomar un descanso, y no terminó lo que iba a hacer. Para evitar que otros entren en la habitación y haciendo un desastre, tiene que usar la única llave en su cuerpo incluso cuando está durmiendo.
Finalmente, algunas personas pueden preguntar, ¿por qué necesita una llave para abrir en lugar de una llave y una puerta? Creo que esto se debe a la complejidad. Por supuesto, una llave y una puerta son más seguras, pero implicará muchos problemas. La generación, almacenamiento, adquisición, devolución, etc. de claves. Su complejidad puede aumentar en las secuencias geométricas con el aumento del método de sincronización, lo que afecta seriamente la eficiencia. Esto también es una compensación. Cuán indeseable es aumentar un poco la seguridad, lo que resulta en una reducción significativa en la eficiencia.
Un simple ejemplo de sincronizado
Public Class TextThread {public static void main (string [] args) {txtThread tt = new txtThread (); nuevo hilo (tt) .Start (); nuevo hilo (tt) .Start (); nuevo hilo (tt) .Start (); nuevo hilo (tt) .Start (); }} class txtThread implementos runnable {int num = 100; String str = new String (); public void run () {SynChronized (str) {while (num> 0) {try {thread.sleep (1); } capt (excepción e) {e.getMessage (); } System.out.println (thread.currentThread (). GetName () + "Esto es" + num--); }}}}En el ejemplo anterior, para crear una diferencia de tiempo, es decir, se usa la oportunidad de cometer un error, hilt.sleep (10).
El mecanismo de soporte y sincronización de Java para la multitud de lectura múltiple es muy popular. Parece que el uso de la palabra clave sincronizada puede resolver fácilmente el problema de la sincronización de datos compartidos multiproceso. ¿Qué exactamente? También es necesario tener una comprensión profunda del papel de las palabras clave sincronizadas antes de que pueda concluir.
En general, la palabra clave sincronizada se puede usar como un modificador de la función o como una declaración dentro de la función, que es el método de sincronización y el bloque de la declaración de sincronización que generalmente se mencionan. Si lo clasifica con más cuidado, Sincronized puede actuar sobre variables de instancia, referencias de objetos, funciones estáticas y literales de clase (constantes literales de nombre de clase).
Antes de dar más detalles, necesitamos aclarar algunos puntos:
R. Si la palabra clave sincronizada se agrega a un método o un objeto, el bloqueo que adquiere es un objeto, en lugar de tratar un código o función como un bloqueo, y es probable que el método de sincronización sea aún más probable.
Acceso de objetos a su hilo.
B. Cada objeto tiene solo un bloqueo asociado con él.
C. La implementación de la sincronización requiere una gran cantidad de gastos generales del sistema como costo e incluso puede causar puntos muertos, así que trate de evitar el control innecesario de sincronización.
A continuación, discutamos el impacto de sincronizado usando diferentes lugares en el código:
Suponiendo que P1 y P2 son objetos diferentes de la misma clase, esta clase define bloques de sincronización o métodos de sincronización en las siguientes situaciones, y P1 y P2 pueden llamarlos.
1. Cuando se usa sincronizado como un modificador de función, el código de ejemplo es el siguiente:
Public sincronizado Void MethodAAA () {//….}Este es el método de sincronización. Entonces, ¿qué objeto está sincronizado bloqueado en este momento? Lo que bloquea es llamar a este objeto de método sincrónico. Es decir, cuando un objeto P1 ejecuta este método de sincronización en diferentes hilos, se formará exclusión mutua entre ellos para lograr el efecto de la sincronización. Sin embargo, otro objeto P2 generado por clase a la que pertenece este objeto puede llamar arbitrariamente a este método con la palabra clave sincronizada agregada.
El código de ejemplo anterior es equivalente al siguiente código:
public void MethodAaa () {Synchronized (this) // (1) {// ... ..}}(1) ¿Qué significa esto en el punto? Se refiere al objeto que llama a este método, como P1. Se puede ver que el método de sincronización es esencialmente aplicar sincronizado a la referencia del objeto. Solo el hilo que ha obtenido el bloqueo del objeto P1 puede llamar al método de sincronización P1. Para P2, el bloqueo P1 no tiene nada que ver con eso. El programa también puede deshacerse del control del mecanismo de sincronización en esta situación, causando confusión de datos: (
2. Sincronizar bloques, el código de muestra es el siguiente:
Public void Method3 (SomeObject SO) {Synchronized (So) {// ... ..}}En este momento, el bloqueo es el objeto de SO. Quien obtenga el bloqueo puede ejecutar el código que controla. Cuando hay un objeto transparente como bloqueo, puede escribir el programa así, pero cuando no hay un objeto claro como bloqueo y solo desea que un código se sincronice, puede crear una variable de instancia especial (tiene que ser un objeto) para actuar como un bloqueo:
clase foo implementa runnable {private byte [] bloquear = new byte [0]; // Variable de instancia especial Public void Metheta () {Synchronized (Lock) {// ...}} // ... ..}NOTA: Los objetos de matriz de bytes de longitud cero son más económicos que cualquier objeto para crear un código de byte compilado: solo se requieren 3 códigos de operación para generar un objeto de byte de longitud cero, mientras que el objeto bloqueo = nuevo objeto () requiere 7 códigos opcodos.
3. Use sincronizado a la función estática, el código de ejemplo es el siguiente:
Clase foo {public sincronizado static void MethodAaa () // Función estática sincronizada {// ... } public void Methodbbb () {Synchronized (foo.class) // class literal (class name literal constante)}}El método MethodBBB () en el código usa class Literal como un bloqueo. Tiene el mismo efecto que la función estática sincronizada. La cerradura obtenida es muy especial. Es la clase a la que pertenece el objeto que actualmente llama este método (clase, no un objeto específico generado por esta clase).
Recuerdo que en el libro "Java efectivo", vi que usar foo.class y p1.getClass () ya que las cerraduras sincrónicas son diferentes, y p1.getClass () no puede usarse para lograr el propósito de bloquear esta clase. P1 se refiere a un objeto generado por la clase FOO.
Se puede inferir que si una clase define una función estática sincronizada A y una función de instancia sincronizada B, entonces el mismo objeto de esta clase no constituirá sincronización al acceder a dos métodos A y B en múltiples hilos, porque sus bloqueos son diferentes. El bloqueo del método A es el objero OBJ, mientras que el bloqueo de B es la clase a la que pertenece OBJ.
El resumen es el siguiente:
Descubrir qué bloqueos sincronizados de objetos puede ayudarnos a diseñar programas más seguros de múltiples subprocesos. También hay algunos consejos para hacer que el acceso sincrónico a los recursos compartidos sea más seguro:
1. Defina la variable de instancia de privado + su método get, en lugar de la variable de instancia de público/protegido. Si una variable se define como pública, el objeto puede evitar el control del método de sincronización y obtenerlo y cambiarlo directamente. Este es también uno de los métodos de implementación 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 un objeto externo obtiene la referencia al objeto de instancia a través del método GET y lo señala a otro objeto, entonces la variable privada cambiará, lo cual no es muy peligroso. En este momento, debe agregar sincronizado al método GET, y solo devolver el clon () de este objeto privado, de modo que la persona que llama obtenga la referencia a la copia del objeto
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.