thread sincronizacion resueltos metodos hilos fuente entre ejemplos diferencia codigo clase java multithreading wait notify

sincronizacion - Hilos de Java: métodos de espera y notificación



runnable java (7)

Tengo un hilo que llama al método de wait y solo se puede despertar cuando se llame al método de notify de alguna otra clase:

class ThreadA { public static void main(String [] args) { ThreadB b = new ThreadB(); b.start(); synchronized(b) { try { System.out.println("Waiting for b to complete..."); b.wait(); } catch (InterruptedException e) {} System.out.println("Total is: " + b.total); } } } class ThreadB extends Thread { int total; public void run() { synchronized(this) { for(int i=0;i<100;i++) { total += i; } notify(); } } }

En el código anterior, si el bloque synchronized en main , si el ThreadA no se ejecuta primero y en su lugar el otro bloque de sincronización se ejecuta y finaliza hasta completarse, ThreadA ejecuta su bloque synchronized y wait , lo que va a pasar y cómo será notificado de nuevo?


1) Debe agregar un indicador que se utiliza para comunicarse entre los hilos, de modo que B pueda enviar una señal a A cuando haya terminado. Una variable booleana simple está bien, siempre y cuando solo se lea y escriba dentro de los bloques sincronizados.

synchronized(this) { for(int i=0;i<100;i++) { total += i; } isDone = true; notify(); }

2) A necesita repetir mientras espera. Entonces, si su variable booleana se llamó isDone, y se configuró en true por threadB, threadA debería tener un código como este:

synchronized(b) { System.out.println("Waiting for b to complete..."); while( ! isDone ) b.wait(); }

En este caso particular, en realidad no hay razón para tener el bloque sincronizado en A, ya que threadB no hace nada después de que termina de ejecutarse, y A no hace nada excepto esperar a B, threadA podría simplemente llamar a b.join () para bloquear hasta que termine. Supongo que su caso de uso real es más complejo que esto.


Es posible que el método de ejecución de ThreadB se complete antes de ingresar al bloque sincronizado en ThreadA.main. En esa situación, dado que la llamada de notificación ha ocurrido antes de que comenzaras a esperar, ThreadA se bloqueará para siempre en la llamada de espera.

Una solución simple sería tomar el bloqueo de b en main antes de iniciar el segundo hilo para garantizar que la espera suceda primero.

ThreadB b = new ThreadB(); synchronized(b) { b.start(); ... b.wait(); }


Probablemente desee usar un semáforo java.util.concurrent.se para esto.


Puede repetir y esperar hasta que se haya computado el total:

synchronized(b) { while (total == 0) { b.wait(); } }

También podría usar una abstracción de nivel superior como CountDownLatch .


Si ThreadB pasa por su bloque synchronized antes que ThreadA , ThreadA se bloqueará indefinidamente en la llamada para wait . No se notificará de ninguna manera que el otro hilo ya se ha completado.

El problema es que está intentando usar wait y notify de manera que no están diseñados para ser utilizados. Usualmente, wait y notify se usan para que un hilo espere hasta que alguna condición sea verdadera, y luego para que otro hilo indique que la condición puede haberse cumplido. Por ejemplo, a menudo se usan de la siguiente manera:

/* Producer */ synchronized (obj) { /* Make resource available. */ obj.notify(); } /* Consumer */ synchronized (obj) { while (/* resource not available */) obj.wait(); /* Consume the resource. */ }

El motivo por el que funciona el código anterior es que no importa qué hilo se ejecuta primero. Si el hilo del productor crea un recurso y nadie está wait el obj , cuando el consumidor lo ejecute, ingresará el ciclo while, notará que el recurso ha sido producido y luego omitirá la llamada para wait . Entonces puede consumir el recurso. Si, por otro lado, el consumidor se ejecuta primero, se dará cuenta en el ciclo while de que el recurso aún no está disponible y wait a que otro objeto lo notifique. El otro subproceso se puede ejecutar, producir el recurso y notify al usuario que el recurso está disponible. Una vez que se despierte el hilo original, notará que la condición del ciclo ya no es verdadera y consumirá el recurso.

De manera más general, Java sugiere que siempre llame a wait en un bucle debido a notificaciones espurias en las que un hilo puede despertarse de una llamada para wait sin que se le notifique nada. Usar el patrón anterior puede evitar esto.

En su caso particular, si desea asegurarse de que ThreadB haya ejecutado antes de que se ejecute ThreadA , puede usar Thread.join() , que bloquea explícitamente el hilo de llamada hasta que se ejecute otro subproceso. En términos más generales, es posible que desee examinar algunas de las primitivas de sincronización proporcionadas por Java, ya que a menudo son mucho más fáciles de usar que wait y notify .


no synchronized(thread) , no lo hagas, no synchronized(thread) .. repat: no synchronized(thread) :)

Y si necesita esperar a que termine el hilo ''b'', use b.join (), ahora su código es libre de colgarse en b.wait ()

-

Afortunadamente, la fuente a continuación puede otorgarle una idea al sincronizar (hilo) / notificar () Considero una mala práctica. (Corte Corte)

Disfrutar

Para proceder a continuación, asegúrese de haber aceptado el acuerdo de licencia de Oracle, que se encuentra allí: https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewLicense- Start? LicenseUUID = 7HeJ_hCwhb4AAAEtmC8ADqmR & ProductUUID = pGqJ_hCwj_AAAAEtB8oADqmS & cnum = & evsref = & sln =

Fuentes Java (incl), llamadas en init (), llamadas efectivamente por cualquier java c-tor, desde java 1.5

private static **synchronized int** nextThreadNum() { return threadInitNumber++; }

// join (el método w / nanos solo aumenta millis por uno, si nanos> 500000, millis == 0 y nanos> 0

public final **synchronized** void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } public **synchronized** void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } }

// se llama a stop1 después de detener asegura privilegios apropiados

private final **synchronized** void stop1(Throwable th) { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if ((this != Thread.currentThread()) || (!(th instanceof ThreadDeath))) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW" if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise stop0(th); } else { // Must do the null arg check that the VM would do with stop0 if (th == null) { throw new NullPointerException(); } // Remember this stop attempt for if/when start is used stopBeforeStart = true; throwableFromStop = th; } }


¿Por qué hacer eso tan complejo? Simplemente use la función join () de Thread.

ThreadB b = new ThreadB(); b.start(); b.join(); // now print b.total