2010-09-02 10 views
11

Je voudrais savoir si Java fournit un équivalent des classes .NET de ManualResetEvent et WaitHandle, car je voudrais écrire du code qui bloque pendant un délai donné à moins qu'un événement ne soit déclenché.Java Equivalent de .NET's ManualResetEvent et WaitHandle

Les classes .NET de WaitHandle et ManualResetEvent fournissent une interface agréable et sans tracas pour ce qui est aussi thread-safe pour autant que je sache, alors qu'est-ce que Java a à offrir?

Répondre

14

Avez-vous envisagé d'utiliser wait/notify (l'équivalent de Monitor.Wait et Monitor.Pulse) à la place?

Vous voulez un peu de vérification pour voir si vous réellement besoin attendre (pour éviter les conditions de course), mais cela devrait fonctionner.

Sinon, quelque chose comme CountDownLatch pourrait bien faire ce que vous voulez.

EDIT: Je viens juste de remarquer que CountDownLatch est fondamentalement "à usage unique" - vous ne pouvez pas réinitialiser le compte plus tard, pour autant que je puisse voir. Vous pouvez vouloir Semaphore à la place. Utilisez tryAcquire comme celui-ci d'attendre avec un délai d'attente:

if (semaphore.tryAquire(5, TimeUnit.SECONDS)) { 
    ... 
    // Permit was granted before timeout 
} else { 
    // We timed out while waiting 
} 

Notez que c'est la différence ManualResetEvent en ce que chaque appel réussi à tryAcquire réduira le nombre de permis - donc finalement ils vont courir à nouveau. Vous ne pouvez pas le faire "définir" de façon permanente comme vous pouvez avec ManualResetEvent. (Cela fonctionne avec CountdownLatch, mais on ne pouvait pas « reset » it :)

+0

Mais qu'en est-il de l'aspect timeout? D'après ce que j'ai lu dans la documentation de CountDownLatch, il ne semble pas y avoir une valeur de délai d'attente que je puisse intercepter ou arrêter avant que le délai expire à la fin. Je voudrais avoir un type Java que je peux utiliser pour attendre un timeout, et qui peut être intercepté avant la fin du timeout, comme un override manuel. Aussi, bonjour M. Skeet! Comment allez vous? Vous en avez probablement beaucoup, mais votre livre est génial! – Orca

+0

@Voulnet: Vous appelez 'await (long timeout, unité TimeUnit)' - qui bloquera pour la durée de timeout donnée * ou * reviendra plus tôt si le loquet est décompté à 0. Heureux que vous aimez le livre :) –

+0

Yup , la solution doit fonctionner à merveille puisque je n'ai qu'un seul objet en attente à tout moment. Merci, monsieur Jon Skeet. – Orca

2

De: http://www.experts-exchange.com/Programming/Languages/Java/Q_22076798.html

Salut, vous pouvez réaliser la synchronisation en utilisant la classe java.util.concurrent.Semaphore (utilisation 0 permis).

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Semaphore.html

exemple ci-dessous vous montre comment résoudre le premier problème de synchronisation, l'autre sera similaire:

import java.util.concurrent.Semaphore; 

class ScalesCommunication { 

    private static Semaphore sem = new Semaphore(0); 

    // called by thread 1 
    void readLoop() { 
     //... 

     //after connection established, release semaphore (value incremented by 1) 
     sem.release(); 
    } 

    // called by thread 2 
    String sendCommand(String command) { 

     sem.acquire(); // thread waits here if sem value == 0 

     // at this point connection is established 
     //... 
    } 
} 
4
class ManualResetEvent { 

    private final Object monitor = new Object(); 
    private volatile boolean open = false; 

    public ManualResetEvent(boolean open) { 
    this.open = open; 
    } 

    public void waitOne() throws InterruptedException { 
    synchronized (monitor) { 
     while (open==false) { 
      monitor.wait(); 
     } 
    } 
    } 

    public void set() {//open start 
    synchronized (monitor) { 
     open = true; 
     monitor.notifyAll(); 
    } 
    } 

    public void reset() {//close stop 
    open = false; 
    } 
} 
+0

Notez que ceci contient un bogue méchant: si 'waitOne()' est appelé avant 'set()' alors le 'monitor' sera entré et donc un appel à' set() 'bloquera, entraînant un blocage. Ma solution hacky consistait à appeler 'monitor.wait (10)' dans une boucle qui quitte la synchronisation à chaque itération. –

+0

En fait, je peux me tromper à ce sujet parce que, apparemment, le moniteur devrait être libéré pendant l'attente, bien que mes tests ont montré une impasse définitive. Peu importe, je suis passé à l'utilisation de 'Semaphore' pour mon cas d'utilisation. –

+0

pourriez-vous vous obtenir des tests avec blocage? – user249654

1

En théorie, la classe ManualResetEvent données ci-dessus est correcte sur Java 5 (mais pas plus tôt). Compte tenu de la longue histoire des implémentations incorrectes (ou inadéquates) de volatile, il me semble plus judicieux d'ajouter un bloc synchronisé supplémentaire dans reset() afin de générer une barrière d'écriture garantie, et d'assurer une atomicité complète. Le danger est qu'une lecture de "open" puisse passer une écriture de "open" sur un processeur Intel multiprocesseur. L'avantage du changement donné ci-dessous: il peut ne pas être efficace de manière optimale, mais il a le grand avantage d'être garanti pour ne pas être faux, à très peu de frais supplémentaires.

class ManualResetEvent { 
     private final Object monitor = new Object(); 
     private volatile boolean open = false; 

     public ManualResetEvent(boolean open) { 
     this.open = open; } 

     public void waitOne() throws InterruptedException { 
     synchronized (monitor) { 
      while (open==false) { 
       monitor.wait(); 
      } 
     } 
     } 

     public void set() {//open start 
     synchronized (monitor) { 
      open = true; 
      monitor.notifyAll(); 
     } 
     } 

     public void reset() {//close stop 
     synchronized(monitor) { 
      open = false; 
     } 
     } 
    } 

Merci à l'auteur original.