2013-07-01 14 views
8

Sto cercando di verificare come funziona wait/notify in java.Sincronizzazione Wait()/notify()

Codice:

public class Tester { 
    public static void main(String[] args) { 
     MyRunnable r = new MyRunnable(); 
     Thread t = new Thread(r); 
     t.start(); 
     synchronized (t) { 
      try { 
       System.out.println("wating for t to complete"); 
       t.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

class MyRunnable implements Runnable { 
    public void run() { 
     System.out.println("entering run method"); 
     synchronized (this) { 
      System.out.println("entering syncronised block"); 
      notify(); 
      try { 
       Thread.currentThread().sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println("leaving syncronized block"); 
     } 
     System.out.println("leaving run method"); 
    } 
} 

output restituiti

wating for t to complete 
entering run method 
entering syncronised block 
//sleep called 
leaving syncronized block 
leaving run method 
wait over 

mi aspettavo quando notify() viene eseguito l'attesa sarà finita & System.out.println("wait over"); andranno stampati. Ma sembra che venga stampato solo quando t ha terminato il suo run().

+5

Non si sta eseguendo la sincronizzazione sugli stessi oggetti – MadProgrammer

+0

MyRunnable.this == r! = T –

+1

@ raul8 La modifica della domanda e l'inserimento nella risposta rendono la risposta corretta non valida. Sarebbe meglio aggiungere un'altra domanda. –

risposta

9

del monitor oggetto serrature devono essere eseguite una sola di riferimento dello stesso blocco ...

Nel tuo esempio si è waiting su un'istanza della Thread, ma usando notify dal Runnable. Invece, si dovrebbe utilizzare un unico, oggetto di blocco comune ... per esempio

public class Tester { 

    public static final Object LOCK = new Object(); 

    public static void main(String[] args) { 
     MyRunnable r = new MyRunnable(); 
     Thread t = new Thread(r); 
     t.start(); 
     synchronized (LOCK) { 
      try { 
       System.out.println("wating for t to complete"); 
       LOCK.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public static class MyRunnable implements Runnable { 

     public void run() { 
      System.out.println("entering run method"); 
      synchronized (LOCK) { 
       System.out.println("entering syncronised block"); 
       LOCK.notify(); 
       try { 
        Thread.currentThread().sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       System.out.println("leaving syncronized block"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

uscita ...

wating for t to complete 
entering run method 
entering syncronised block 
leaving syncronized block 
wait over 
leaving run method 

wait over e leaving run method potrebbe cambiare posizione a seconda della programmazione dei thread.

Si potrebbe provare a mettere il lato sleep fuori il blocco synchronized. Questo rilascerà il blocco del monitor che permette la sezione wait di continuare a funzionare (in quanto non può iniziare fino a quando il blocco viene rilasciato)

public static class MyRunnable implements Runnable { 

     public void run() { 
      System.out.println("entering run method"); 
      synchronized (LOCK) { 
       System.out.println("entering syncronised block"); 
       LOCK.notify(); 
       System.out.println("leaving syncronized block"); 
      } 
      try { 
       Thread.currentThread().sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
+0

grazie ... ma ho provato il tuo codice .. ma non funziona ancora. Codice in questione aggiornato – reiley

+0

Sembra funzionare bene. Cosa ti aspetti? – MadProgrammer

+0

FYI '' Thread.sleep'' (entrambi i moduli) è un metodo statico che mette sempre in attesa il thread chiamante; quindi '' Thread.currentThread(). sleep (1000) '' è semanticamente ridondante e probabilmente fuorviante (ad esempio chiamando '' t.sleep (1000) '' metterebbe il thread chiamante a dormire, non t). – kbolino

5

risposta al codice aggiornato:

Da Thread.sleep() javadoc:

Causa il thread attualmente in esecuzione di sospensione (interruzione temporanea dell'esecuzione) per il numero specificato di millisecondi , soggetto alla precisione e all'accuratezza dei timer di sistema e agli scheduler. Il thread non perde la proprietà di alcun monitor.

Se si chiama Thread.sleep mentre all'interno di un blocco sincronizzato, altri thread non saranno in grado di entrare nel blocco sincronizzato. Non dovresti mai svolgere attività che richiedono molto tempo mentre sei in un blocco sincronizzato per evitare questo.

1

Nota (come altri hanno sottolineato anche) che è necessario utilizzare lo stesso oggetto per il blocco/sincronizzazione in entrambi i thread.

Se si desidera che il thread principale continui immediatamente dopo che è stato chiamato notify, è necessario rinunciare temporaneamente al blocco. Altrimenti, wait verrà chiamato solo dopo che il thread secondario lascia il blocco synchronized. E non è mai una buona idea tenere un lucchetto in un calcolo a lungo termine!

Un modo come raggiungere è quello di utilizzare wait(int) sulla serratura invece di sleep, perché wait rilascia il blocco di sincronizzazione temporanea:

public class Tester { 
    private static final Object lock = new Object(); 

    public static void main(String[] args) { 
     Thread t = new Thread(new MyRunnable()); 
     t.start(); 
     synchronized (lock) { 
      try { 
       System.out.println("wating for t to complete"); 
       lock.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    static class MyRunnable implements Runnable { 
     public void run() { 
      System.out.println("entering run method"); 
      synchronized (lock) { 
       System.out.println("entering syncronised block"); 
       lock.notify(); 
       try { 
        lock.wait(1000); // relinquish the lock temporarily 
       } catch (InterruptedException ex) { 
        System.out.println("got interrupted"); 
       } 
       System.out.println("leaving syncronized block"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

Tuttavia, utilizzando queste primitive di basso livello può essere molto soggetto ad errori e ho sconsigliarne l'uso. Invece, ti suggerirei di usare i primitivi di alto livello di Java per questo. Ad esempio, è possibile utilizzare CountDownLatch che permette un'attesa thread fino a quando altri thread contano fino a zero:

import java.util.concurrent.*; 

public class TesterC { 
    private static final CountDownLatch latch = new CountDownLatch(1); 

    public static void main(String[] args) { 
     Thread t = new Thread(new MyRunnable()); 
     t.start(); 

     System.out.println("wating for t to complete"); 
     try { 
      latch.await(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("wait over"); 
    } 

    static class MyRunnable implements Runnable { 
     public void run() { 
      System.out.println("entering run method"); 
      try { 
       latch.countDown(); 
       Thread.sleep(1000); 
      } catch (InterruptedException ex) { 
       System.out.println("got interrupted"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

Qui non c'è bisogno di sincronizzare qualsiasi cosa, il fermo fa tutto per te. Esistono molti altri primitivi che puoi usare: semafori, uno scambiatore, code thread-safe, ecc. Esplora il pacchetto java.util.concurrent.

Forse la soluzione migliore è utilizzare anche API di livello superiore, come ad esempio Akka. Qui si lavora con Actors o Software transactional memory, che può essere composto facilmente e risparmiato dalla maggior parte dei problemi di concorrenza.