0. Con respecto a la sincronización de hilos (1) ¿Por qué necesitamos sincronizar la lectura múltiple?
La sincronización de hilos se refiere a permitir que múltiples hilos en ejecución cooperen bien para permitir que múltiples hilos ocupen y liberen recursos razonablemente según sea necesario. Utilizamos bloques de código de sincronización y métodos de sincronización en Java para lograr este objetivo. Por ejemplo, resuelva el problema de la ejecución de orden no fijo múltiple:
public class twothreadTest {public static void main (string [] args) {Thread th1 = new MyThread1 (); Thread th2 = new MyThread2 (); Th1.Start (); Th2.Start (); }} clase MyThread2 extiende el hilo {@Override public void run () {for (int i = 0; i <10; i ++) sistema. out.println ("Hilo 1 contador:"+i); }} clase MyThread1 extiende el hilo {@Override public void run () {for (int i = 0; i <10; i ++) sistema. out.println ("Hilo 1 contador:"+i); }} clase MyThread1 extiende el hilo {@Override public void run () {for (int i = 0; i <10; i ++) sistema. out.println ("contador de hilo 2:"+i); }}El resultado de la ejecución múltiple en este estado es insertar la ejecución al azar a voluntad, que depende completamente de la programación del hilo de JVM. En muchos casos en los que se requiere ejecución ordenada, este estado de ejecución aleatoria obviamente es inadecuado.
public class Threadtest {public static void main (string [] args) {mythread thread = new mythread (); Hilo th1 = nuevo hilo (hilo); Hilo th2 = nuevo hilo (hilo); Th1.Start (); Th2.Start (); }} class myThread implementos runnables {@Override public sincronized void run () {for (int i = 0; i <10; i ++) sistema. out.println (Thread. CurrentThread (). getName ()+"contador:"+i); }}Después de usar el método de sincronización, podemos controlar el hilo para ocupar exclusivamente el objeto del cuerpo de ejecución. De esta manera, durante el proceso de ejecución, el hilo puede ejecutar las tareas en el cuerpo de ejecución a la vez y salir del estado de bloqueo. El JVM luego envía otro hilo para ejecutar las tareas en el cuerpo de ejecución a la vez.
(2) El paradigma para la creación de hilos y la ejecución:
En el pasado, también teníamos nuestro propio paradigma de programación para la creación y ejecución de hilos. En general, definimos una clase de ejecución para reescribir el método run (), pero este método reúne el cuerpo de ejecución y las tareas ejecutadas, que no conducen al desacoplamiento desde la perspectiva de la ingeniería de software. La ejecución de un hilo significa que un hilo ejecuta una tarea de un objeto a través del objeto de ejecución. Desde esta perspectiva, separar el prescriptor de la tarea de la clase de ejecución puede dejar claros los diversos roles de la programación múltiple y así obtener un buen desacoplamiento. El siguiente es el paradigma de programación para la creación y ejecución de los hilos:
clase pública formalThreadClass {public static void main (string [] args) {Thread Thread = new Thread (new MyRunnable ()); Thread.Start (); }} class Myrunnable implementos runnable {myTask myTask = new myTask (); @Override public void run () {myTask.Dotask (); }} clase myTask {public void dotask () {Sistema. out.println ("Esta es una tarea real"); }}
1. Principio sincronizado
En Java, cada objeto tiene y solo tiene un bloqueo de sincronización. Esto también significa que el bloqueo de sincronización existe en el objeto.
Cuando llamamos el método sincronizado de un objeto, adquirimos el bloqueo de sincronización del objeto. Por ejemplo, Sincronized (OBJ) adquiere el bloqueo de sincronización del "objeto OBJ".
El acceso a los bloqueos de sincronización por diferentes hilos es mutuamente excluyente. ¡En otras palabras, en cierto punto en el tiempo, el bloqueo de sincronización del objeto solo se puede obtener mediante un hilo! A través de bloqueos de sincronización, podemos lograr un acceso mutuamente excluyente a "objetos/métodos" en múltiples hilos. Por ejemplo, ahora hay dos hilos A y Hilt B, que acceden al "bloqueo sincrónico de Obj OBJ". Supongamos que en algún momento, el enhebrado A adquiere el "bloqueo de sincronización de OBJ" y realiza algunas operaciones; En este momento, el hilo B también intenta adquirir el "bloqueo de sincronización de OBJ": el hilo B no puede adquirir, debe esperar hasta que el hilo A libere el "bloqueo de sincronización de OBJ" y solo se puede ejecutar.
2. Reglas básicas sincronizadas
Resumimos las reglas básicas de sincronizado en los siguientes 3 e ilustramos a través de ejemplos.
Artículo 1: Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros subprocesos se bloquearán desde el acceso al "método sincronizado" o "bloque de código sincronizado" de "el objeto".
Artículo 2: Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros subprocesos aún pueden acceder al bloque de código asincronizado de "este objeto".
Artículo 3: Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros subprocesos se bloquearán para acceder a otros "métodos sincronizados" o "bloque de código sincronizado" de "el objeto".
(1) Artículo 1:
Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros hilos se bloquearán desde el acceso al "método sincronizado" o "bloque de código sincronizado" de "el objeto". A continuación se muestra el programa de demostración correspondiente al "bloque de código sincronizado".
Class Myrunable Implements runnable {@Override public void run () {sincronizado (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName () + "Loop" + I); }} capt (interruptedException IE) {}}}} public class Demo1_1 {public static void main (string [] args) {runnable demo = new Myrunable (); // crear un nuevo hilo "objeto runnable" t1 = nuevo hilo (demo, "t1"); // Crear un nuevo "Hilo T1", T1 se basa en el hilo de objeto Runnable T2 = New Thread (demo, "T2"); // Crear un nuevo "Hilo T2", T2 se basa en el objeto ejecutable T1.Start (); // Iniciar "Hilo T1" T2.Start (); // Iniciar "Hilo T2"}} Resultados de ejecución:
T1 Loop 0T1 Loop 1T1 Loop 2T1 Loop 3T1 Loop 4T2 Loop 0T2 Loop 1T2 Loop 2T2 Loop 3T2 Loop 4
El resultado muestra que hay un "bloque de código sincronizado (este)" en el método run (), y T1 y T2 son hilos creados en función del objeto ejecutable "demo". Esto significa que podemos considerar esto sincronizado (esto) como "objeto de demostración ejecutable"; Por lo tanto, los hilos T1 y T2 comparten "Bloqueo sincrónico del objeto de demostración". Por lo tanto, cuando se ejecuta un hilo, otro hilo debe esperar a que el "hilo en ejecución" libere el "bloqueo de sincronización de demostración" antes de que pueda ejecutarse.
Si confirma, descubrió este problema. Luego modificamos el código anterior y luego lo ejecutamos para ver cómo es el resultado y vemos si se confunde. El código fuente modificado es el siguiente:
La clase MyThread extiende el hilo {public mythread (name de cadena) {super (nombre); } @Override public void run () {sincronizado (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName () + "Loop" + I); }} capt (interruptedException IE) {}}}} public class Demo1_2 {public static void main (string [] args) {Thread t1 = new Mythread ("t1"); // crear nuevo "hilo t1" hilo t2 = nuevo mythread ("t2"); // Crear nuevo "Hilo T2" T1.Start (); // Iniciar "Hilo T1" T2.Start (); // Iniciar "Hilo T2"}} Descripción del código: Comparación de demo1_2 y demo1_1, descubrimos que la clase Mythread en Demo1_2 se hereda directamente del hilo, y T1 y T2 son hilos infantiles mythread.
Afortunadamente, el método "run () de demo1_2" también llamado sincronizado (esto), al igual que el método "run () de demo1_1" también llamado sincronizado (esto).
Entonces, ¿el proceso de ejecución de Demo1_2 es el mismo que Demo1_1? Resultados de ejecución:
T1 Loop 0T2 Loop 0T1 Loop 1T2 Loop 1T1 Loop 2T2 Loop 2T1 Loop 3T2 Loop 3T1 Loop 4T2 Loop 4
Descripción de los resultados:
Si este resultado no te sorprende en absoluto, entonces creo que tienes una comprensión más profunda de sincronizado y esto. De lo contrario, continúe leyendo el análisis aquí.
Esto sincronizado (esto) se refiere al "objeto de clase actual", es decir, el objeto actual correspondiente a la clase donde se encuentra sincronizado (esto). Su propósito es obtener el "bloqueo sincrónico del objeto actual".
Para Demo1_2, esto está sincronizado (esto) representa el objeto Mythread, mientras que T1 y T2 son dos objetos mythread diferentes. Por lo tanto, cuando T1 y T2 se ejecutan sincronizados (esto), adquieren los bloqueos de sincronización de diferentes objetos. Para el par demo1_1, esto está sincronizado (esto) representa el objeto Myrunable; T1 y T2 comparten un objeto mironable. Por lo tanto, un hilo adquiere el bloqueo de sincronización del objeto, lo que hará que espere otro hilo.
(2) Artículo 2:
Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros hilos aún pueden acceder al bloque de código asincronizado de "este objeto".
A continuación se muestra el programa de demostración correspondiente al "bloque de código sincronizado".
Class Count {// Métodos que contienen sincronización sincronizada bloquea public void synmethod () {sincronizado (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // dormir para 100MS System.out.println (Thread.CurrentThread (). GetName () + "Synmethod Loop" + I); }} capt (interruptedException IE) {}}} // método asíncrono public nodsynmethod () {try {for (int i = 0; i <5; i ++) {Thread.sleep (100); System.out.println (Thread.CurrentThread (). GetName () + "Nonsynmethod Loop" + i); }} Catch (InterruptedException IE) {}}} public class Demo2 {public static void main (string [] args) {count final count = new Count (); // Crear nuevo T1, T1 llamará al método synmethod () del hilo "cuente" hilo t1 = nuevo hilo (new runnable () {@Override public void run () {count.synmethod ();}}, "t1"); // Crear un nuevo T2, T2 llamará al método NonSynmethod () del hilo "Conteo Object" T2 = New Thread (new Runnable () {@Override public void run () {count.nonsynmethod ();}}, "T2"); t1.start (); // Iniciar T1 T2.Start (); // Iniciar T2}} Resultados de ejecución:
T1 Synmethod Loop 0T2 NonnynMethod Loop 0T1 Synmethod Loop 1T2 Nonnynmethod Loop 1T1 Synmethod Loop 2T2 Nonsynmethod Loop 2T1 Synmethod Loop 3T2 Nonsynmethod Loop 3T1 Synmethod Loop 4T2 Nonsynmethod Loop 4
Descripción de los resultados:
Dos nuevos hilos infantiles T1 y T2 se crean en el hilo principal. T1 llamará al método synmethod () del objeto de conteo, que contiene bloques de sincronización; T2 llamará al método noynmethod () del objeto de conteo, que no es un método de sincronización. Cuando T1 se está ejecutando, aunque se llama sincronizado (esto) para obtener el "bloqueo de sincronización de recuento"; No hace que T2 se bloquee porque T2 no usa el bloqueo de sincronización de "recuento".
(3) Artículo 3:
Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", se bloqueará otros hilos a otros "métodos sincronizados" o "bloque de código sincronizado" del "objeto".
También modificaremos el cuerpo del método NonsynMethod () en el ejemplo anterior con sincronizado (esto). El código fuente modificado es el siguiente:
Class Count {// Métodos que contienen sincronización sincronizada bloquea public void synmethod () {sincronizado (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // dormir para 100MS System.out.println (Thread.CurrentThread (). GetName () + "Synmethod Loop" + I); }} capt (interruptedException es decir) {}}} // Los métodos que contienen sincronización sincronizada bloquean public sinonynmethod () {sincronizado (this) {try {para (int i = 0; i <5; i ++) {horth.slele (100); System.out.println (Thread.CurrentThread (). GetName () + "Nonsynmethod Loop" + i); }} Catch (InterruptedException IE) {}}}} public class Demo3 {public static void main (string [] args) {count final count = new Count (); // Crear T1, T1 llamará al método synmethod () del hilo "cuente" hilo t1 = nuevo hilo (new runnable () {@Override public void run () {count.syncmethod ();}}, "t1"); // Crear nuevo T2, T2 llamará al método NonSynmethod () del hilo "Conteo Object" T2 = New Thread (new Runnable () {@Override public void run () {count.nonsynmethod ();}}, "T2"); t1.start (); // Iniciar T1 T2.Start (); // Iniciar T2}} Resultados de ejecución:
T1 Synmethod Loop 0T1 Synmethod Loop 1T1 Synmethod Loop 2T1 Synmethod Loop 3T1 Synmethod Loop 4T2 Nonnynmethod Loop 0t2 Nonsynmethod Loop 1T2 Nonsynmethod Loop 2T2 NonsynMethod Loop 3T2 NonsynMethod Loop 4
Descripción de los resultados:
Dos nuevos hilos infantiles T1 y T2 se crean en el hilo principal. Tanto la llamada T1 como T2 se sincronizan (esto), que es un objeto de conteo (recuento), y T1 y T2 comparten el recuento. Por lo tanto, cuando se ejecuta T1, T2 se bloqueará y T1 se ejecutará para liberar el "bloqueo sincrónico del objeto de conteo" antes de que T2 pueda ejecutarse.
3. Método sincronizado y bloque de código sincronizado
El "Método sincronizado" utiliza el método de modificación sincronizado, mientras que el "bloque de código sincronizado" utiliza el bloque de código de modificación sincronizado.
Ejemplo de método sincronizado
public sincronizado void foo1 () {system.out.println ("método sincronizado");} bloque de código sincronizado public void foo2 () {sincronizado (this) {system.out.println ("método sincronizado"); }} Esto en el bloque de código sincronizado se refiere al objeto actual. Esto también se puede reemplazar con otros objetos, como este se reemplaza con OBJ, luego Foo2 () adquiere el bloqueo de sincronización de OBJ cuando se sincroniza (OBJ).
Los bloques de código sincronizados pueden controlar las áreas de acceso restringidas al conflicto con mayor precisión y, a veces, funcionar de manera más eficiente. Aquí hay un ejemplo para demostrar:
// Demo4.Java's Source Code public class Demo4 {public sincronizado void synmethod () {for (int i = 0; i <1000000; i ++); } public void synblock () {sincronizado (this) {for (int i = 0; i <1000000; i ++); }} public static void main (string [] args) {demo4 demo = new Demo4 (); comienzo largo, diff; start = System.CurrentTimemillis (); // Obtenga la hora actual (Millis) demo.syncmethod (); // llamar "método sincronizado" diff = system.currentTimemillis () - inicio; // Obtener "diferencia de tiempo" System.out.println ("SyncMethod ():"+ Diff); start = System.CurrentTimemillis (); // Obtenga la hora actual (Millis) demo.syncblock (); // Llame al "bloque de método sincronizado" diff = System.CurrentTimemillis () - inicio; // Obtener "diferencia de tiempo" system.out.println ("syncblock ():"+ diff); }} (Una vez) Resultado de ejecución:
synmethod (): 11Synblock (): 3
4. Bloqueo de instancia y bloqueo global
Bloqueo de instancia: bloqueado en un objeto de instancia. Si la clase es un singleton, entonces el bloqueo también tiene el concepto de un bloqueo global.
(1) La palabra clave sincronizada corresponde al bloqueo de instancia.
(2) Bloqueo global: el bloqueo está dirigido a una clase. No importa cuántos objetos sea la instancia, los hilos comparten el bloqueo.
El bloqueo global corresponde a static sincronizado (o bloqueado en la clase o objeto de cargador de clase de esta clase).
Hay un ejemplo muy vívido de "bloqueo de instancia" y "bloqueo global":
Clase pulbica algo {public sincronizado Void isSynca () {} public sincronizado Void isSyncb () {} public static sincronizado void csynca () {} public sincronizado void sincronizado sincronizado csyncb () {}} Supongamos que algo tiene dos instancias x e y. Analice las cerraduras adquiridas por los siguientes cuatro conjuntos de expresiones.
(1) x.issynca () y x.issyncb ()
(2) x.issynca () y y.issynca ()
(3) x.csynca () y y.csyncb ()
(4) x.issynca () y algo.csynca ()
(1) No se puede acceder simultáneamente.
¡Porque isSynca () e isSyncb () son bloqueos de sincronización que acceden al mismo objeto (objeto X)!
// LOCKTEST1. Java's Source Code Class Something {public sincronizado void issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": IsSynca"); }} capt (interruptedException IE) {}} public sincronizado void isSyncb () {try {para (int i = 0; i <5; i ++) {thread.sleep (100); // dormir para 100MS System.out.println (Thread.CurrentThread (). GetName ()+": IsSyncb"); }} capt (interruptedException IE) {}}} public class LockTest1 {something x = new Something (); Algo y = nuevo algo (); // comparar (01) x.issynca () con x.issyncb () private void test1 () {// crea nuevo t11, t11 llamará x.issynca () hilo t11 = new thread (new runnable () {@Override public void run () {x.issynca ();}}, "t11"); // Crear nuevo T12, T12 llamará a X.issyncb () Thread t12 = new Thread (new Runnable () {@Override public void run () {x.issyncb ();}}, "t12"); t11.start (); // Iniciar T11 T12.Start (); // Iniciar T12} public static void main (string [] args) {LockTest1 demo = new LockTest1 (); demo.test1 (); }} Resultados de ejecución:
T11: ISSYNCAT11: ISSYNCAT11: ISSYNCAT11: ISSYNCAT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCB
(2) se puede acceder al mismo tiempo
Debido a que no está accediendo al bloqueo de sincronización del mismo objeto, x.issynca () accede al bloqueo de sincronización de x, mientras que Y.issynca () accede al bloqueo de sincronización de y.
// Locktest2.Java's Source Code Class Something {public sincronizado vacío issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": IsSynca"); }} capt (interruptedException IE) {}} public sincronizado void isSyncb () {try {para (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": isSyncb"); }} capt (interruptedException IE) {}} public static sincronizado void csynca () {try {for (int i = 0; i <5; i ++) {Thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": csynca"); }} capt (interruptedException IE) {}} public static sincronizado void csyncb () {try {for (int i = 0; i <5; i ++) {Thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": csyncb"); }} capt (interruptedException IE) {}}} public class LockTest2 {something x = new Something (); Algo y = nuevo algo (); // Compare (02) x.issynca () con y.issynca () private void test2 () {// crea nueva t21, t21 llamará x.issynca () Thread t21 = new Thread (new runnable () {@Override public void run () {x.issynca ();}}, "T21"); // Crear nuevo T22, T22 llamará a X.issyncb () Thread t22 = new Thread (new Runnable () {@Override public void run () {y.issynca ();}}, "t22"); t21.start (); // Iniciar T21 T22.Start (); // Iniciar T22} public static void main (string [] args) {LockTest2 demo = new LockTest2 (); demo.test2 (); }} Resultados de ejecución:
T21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCA
(3) no se puede acceder simultáneamente
Debido a que csynca () y csyncb () son tipos estáticos, x.csynca () es equivalente a algo.issynca (), y y.csyncb () es equivalente a algo.issyncb (), comparten un bloqueo de sincronización y no se pueden preguntar al mismo tiempo.
// Locktest3.Java's Source Code Class Something {public sincronizado vacío issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": IsSynca"); }} capt (interruptedException IE) {}} public sincronizado void isSyncb () {try {para (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": isSyncb"); }} capt (interruptedException IE) {}} public static sincronizado void csynca () {try {for (int i = 0; i <5; i ++) {Thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": csynca"); }} capt (interruptedException IE) {}} public static sincronizado void csyncb () {try {for (int i = 0; i <5; i ++) {Thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": csyncb"); }} capt (interruptedException IE) {}}} public class LockTest3 {algo x = nuevo algo (); Algo y = nuevo algo (); // Compare (03) x.csynca () con y.csyncb () private void test3 () {// crea nuevo t31, t31 llamará x.issynca () Thread t31 = new Thread (new runnable () {@Override public void run () {x.csynca ();}}, "T31"); // Crear nuevo T32, T32 llamará a X.Issyncb () Thread t32 = new Thread (new Runnable () {@Override public void run () {y.csyncb ();}}, "t32"); t31.Start (); // Iniciar T31 T32.Start (); // Iniciar T32} public static void main (string [] args) {LockTest3 demo = new LockTest3 (); demo.test3 (); }} Resultados de ejecución:
t31: cSyncAt31: cSyncAt31: cSyncAt31: cSyncAt31: cSyncAt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: CSYNCB
(4) se puede acceder simultáneamente
Debido a que isnynca () es un método de instancia, X.issynca () usa el bloqueo del objeto X; Mientras que csynca () es un método estático, algo.csynca () puede entender que es un "bloqueo de clase" utilizado. Por lo tanto, se puede acceder simultáneamente.
// Locktest4.Java's Source Code Class Something {public sincronizado void issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": IsSynca"); }} capt (interruptedException IE) {}} public sincronizado void isSyncb () {try {para (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": isSyncb"); }} capt (interruptedException IE) {}} public static sincronizado void csynca () {try {for (int i = 0; i <5; i ++) {Thread.sleep (100); // Sleep 100ms System.out.println (Thread.CurrentThread (). GetName ()+": csynca"); }} capt (interruptedException IE) {}} public static sincronizado void csyncb () {try {for (int i = 0; i <5; i ++) {Thread.sleep (100); // dormir para 100MS System.out.println (Thread.CurrentThread (). GetName ()+": csyncb"); }} catch (interruptedException IE) {}}} public class LockTest4 {something x = new Something (); Algo y = nuevo algo (); // Compare (04) x.issynca () con algo.csynca () private void test4 () {// crea nuevo t41, t41 llamará x.issynca () hilo t41 = new Thread (new Runnable () {@Override public void run () {x.issynca ();}, "T41"); // Crear nuevo T42, T42 llamará a X.Issyncb () Thread t42 = new Thread (new Runnable () {@Override public void run () {something.csynca ();}}, "t42"); t41.start (); // Iniciar T41 T42.Start (); // Iniciar T42} public static void main (string [] args) {LockTest4 demo = new LockTest4 (); demo.test4 (); }} Resultados de ejecución:
T41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCA