2015-12-15 1 views
-3

J'ai 2 threads que je veux synchroniser avec wait() et notify(). Cependant, lorsque je notifie le thread qui attend ne reprend jamais. Ce sont mes morceaux de code. Dans Lib60870, je démarre les deux threads et le thread HandShake est synchronisé avec SerialReader.notify() et wait() ne fonctionnant pas en Java

public Lib60870(){ //Here I start threads 
    try { 
     myConnection=new Connection(LOCALHOST,port); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

    mySerialReader.start(); 
    myHandshake.start();} 
} 

Classe SerialReader

public class SerialReader extends Thread { 

private static boolean isPaused=true; 

@Override 
public void run() { 
    synchronized(this){ 
     if(Lib60870.myConnection!=null){ 
      while(true){ 
       if(!isPaused){ 
        byte inByte=Lib60870.myConnection.getByte(); 
        if(inByte==0x68){ 
         ... 
         } 
         notify(); 
        } 
        else if(inByte==0x10){ 
         ...   
         } 
         notify(); 
        } 
       } 
      } 
     } 
    } 
} 

public void setPause(boolean pause){ 
    isPaused=pause; 
} 

Classe Handshake

public class HandShake extends Thread { 


public void run() { 
    synchronized(Lib60870.mySerialReader){ 
     Lib60870.mySerialReader.setPause(false); 
     ... 
     try { 
      Lib60870.mySerialReader.wait(); 
     } catch (InterruptedException e1) { 
      // TODO Auto-generated catch block 
      e1.printStackTrace(); 
     } 
     Lib60870.mySerialReader.setPause(true); 
     ... 
     Lib60870.mySerialReader.setPause(false); 
     try { 
      Lib60870.mySerialReader.wait(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
} 

}

Merci à l'avance

+0

Veuillez publier un [Exemple minimal, complet et vérifiable] (http://stackoverflow.com/help/MCVE). –

Répondre

0

Le principal problème que vous avez est qu'un seul thread peut contenir un verrou. Cela signifie que tant que votre thread notify() contient le verrou, aucun autre thread ne peut être exécuté dans un bloc de code qui contient ce verrou.

Déplacez le bloc synchronisé à l'intérieur du bloc if (isPaused) afin qu'un autre thread puisse s'exécuter entre les deux.

Un autre problème que vous avez est que votre booléen isPaused n'est pas volatil, donc il peut être en ligne, c'est-à-dire qu'il pourrait ne jamais s'arrêter. Il ne devrait pas être statique même si vous savez que vous n'en aurez jamais plus d'un, c'est une mauvaise habitude d'utiliser un champ statique pour une variable d'instance.

En cas de pause, veillez à ce que la CPU soit en veille.

Chaque fois que vous notifiez() ou notezAll() cela devrait provoquer un changement d'état, wait() devrait toujours vérifier ce changement d'état.

1

Il y a beaucoup de problèmes avec votre approche:

  1. L'extension Thread est considéré comme une mauvaise pratique.
  2. En utilisant wait/notify pour quelque chose qui peut être fait avec quelque chose dans java.util.concurrent n'est pas une bonne idée.
  3. La chute dans une boucle à rotation serrée n'est pas en pause.

Voici une classe de threads pouvant être mise en suspens. Ecrivez-vous un objet Stepper et utilisez l'un de ces objets pour exécuter la méthode step() sans interruption. Utilisez ses méthodes pause/resume pour le mettre en pause proprement.

/** 
* PauseableThread is a Thread with pause/resume and cancel methods. 
* 
* The meat of the process must implement `step`. 
* 
* You can either extend this and implement `step` or use the factory. 
* 
* I cannot extend Thread because my resume will clash. 
* 
*/ 
public abstract class PauseableThread implements Runnable { 

    // The lock. 
    private final ReadWriteLock pause = new ReentrantReadWriteLock(); 
    private final Lock readLock = pause.readLock(); 
    private final Lock writeLock = pause.writeLock(); 
    // Flag to cancel the whole process. 
    private volatile boolean cancelled = false; 
    // The exception that cause it to finish. 
    private Exception thrown = null; 
    // The thread that is me. 
    private Thread me = null; 

    @Override 
    // The core run mechanism. 
    public void run() { 
     // Track my current thread. 
     me = Thread.currentThread(); 
     try { 
      while (!finished()) { 
       // Block here if we're paused. 
       blockIfPaused(); 
       // Don't do any more work if we've been asked to stop. 
       if (!finished()) { 
        // Do my work. 
        step(); 
       } 
      } 
     } catch (Exception ex) { 
      // Just fall out when exception is thrown. 
      thrown = ex; 
     } 
    } 

    // Have we finished yet? 
    private boolean finished() { 
     return cancelled || me.isInterrupted(); 
    } 

    // Block if pause has been called without a matching resume. 
    private void blockIfPaused() throws InterruptedException { 
     try { 
      // Grab a write lock. Will block if a read lock has been taken - i.e. we've been paused. 
      writeLock.lockInterruptibly(); 
     } finally { 
      // Release the lock immediately to avoid blocking when pause is called. 
      writeLock.unlock(); 
     } 
    } 

    // Pause the work. NB: MUST be balanced by a resume. 
    public void pause() { 
     // We can wait for a lock here. 
     readLock.lock(); 
    } 

    // Resume the work. NB: MUST be balanced by a pause. 
    public void resume() { 
     // Release the lock. 
     readLock.unlock(); 
    } 

    // Stop. 
    public void cancel() { 
     // Stop everything. 
     cancelled = true; 
    } 

    // Stop immediately (if param is true). 
    public void cancel(boolean interrupt) { 
     if (interrupt) { 
      // Interrupt me. 
      me.interrupt(); 
     } else { 
      // Or cancel me. 
      cancel(); 
     } 
    } 

    // Wait for completion. 
    public void await() throws InterruptedException { 
     // Wait 'till we've finished. NB: Will wait forever if you haven't instigated a cancel of some kind. 
     while (me.isAlive()) { 
      Thread.sleep(0); 
     } 
    } 

    // Start - like a thread. 
    public void start() { 
     // Wrap me in a thread and fire the sucker up! 
     new Thread(this).start(); 
    } 

    // Get the exception that was thrown to stop the thread or null if the thread was cancelled. 
    public Exception getThrown() { 
     return thrown; 
    } 

    // Expose my Thread. 
    public Thread getThread() { 
     return me; 
    } 

    // Create this method to do stuff. 
    // Calls to this method will stop when pause is called. 
    // Any thrown exception stops the whole process. 
    public abstract void step() throws Exception; 

    // Factory to wrap a Stepper in a PauseableThread 
    public static PauseableThread make(Stepper stepper) { 
     StepperThread pauseableStepper = new StepperThread(stepper); 
     // That's the thread they can pause/resume. 
     return pauseableStepper; 
    } 

    // One of these must be used. 
    public interface Stepper { 

     // A Stepper has a step method. 
     // Any exception thrown causes the enclosing thread to stop. 
     public void step() throws Exception; 
    } 

    // Holder for a Stepper. 
    private static class StepperThread extends PauseableThread { 

     // The actual stepper I am proxying. 
     private final Stepper stepper; 

     StepperThread(Stepper stepper) { 
      this.stepper = stepper; 
     } 

     @Override 
     public void step() throws Exception { 
      stepper.step(); 
     } 
    } 

    // !!!! Testing only below !!!! 
    // My test counter. 
    static int n = 0; 

    // Test/demo. 
    public static void main(String[] args) throws InterruptedException { 

     try { 
      // Simple stepper that just increments n. 
      Stepper s =() -> { 
       n += 1; 
       Thread.sleep(1); 
      }; 
      PauseableThread pt = PauseableThread.make(s); 
      // Start it up. 
      pt.start(); 
      Thread.sleep(1000); 
      pt.pause(); 
      System.out.println("Paused: " + n); 
      Thread.sleep(1000); 
      System.out.println("Resuminng: " + n); 
      pt.resume(); 
      Thread.sleep(1000); 
      pt.cancel(); 
      pt.await(); 
      System.out.println("Finished: " + n); 

      // Start again to test agressive cancelling. 
      n = 0; 
      pt = PauseableThread.make(s); 
      // Start it up. 
      pt.start(); 
      Thread.sleep(1000); 
      pt.pause(); 
      System.out.println("Paused: " + n); 
      Thread.sleep(1000); 
      System.out.println("Resuminng: " + n); 
      pt.resume(); 
      Thread.sleep(1000); 
      // Cancel aggressively. 
      pt.cancel(true); 
      pt.await(); 
      System.out.println("Finished: " + n); 
      System.out.println("thrown: " + pt.getThrown()); 

     } catch (InterruptedException e) { 
     } 
    } 
} 
+0

"L'extension du fil est considérée comme une mauvaise pratique." ... à la place, vous devriez implémenter Runnable et créer un nouveau thread à partir de là. Ou utilisez quelque chose qui crée de nouveaux threads pour vous, comme un 'ExecutorService'. – Powerlord

+0

Votre solution est trop compliquée. Simple CountdownLatch partagé entre deux threads résoudra le problème d'attente et de notification. – Gaskoin