2016-04-12 15 views
1

Ich nehme eine Integer-Variable und Freigabe mit zwei Threads. Ein Thread sollte gerade Zahlen und ein Thread sollte ungerade Zahl nacheinander drucken. Aber notify() werfen IllegalMonitorStateException.java warten und benachrichtigen

package mywaitnotifytest; 
public class App { 

    public static void main(String[] args) { 
     Integer i=0; 
     Even even = new Even(i); 
     even.setName("EvenThread"); 
     Odd odd = new Odd(i); 
     odd.setName("OddThread"); 
     even.start(); 
     odd.start(); 
    } 
} 

class Even extends Thread{ 

    Integer var; 

    Even(Integer var){ 
     this.var=var; 
    } 

    @Override 
    public void run() { 
     while(true){ 
      synchronized (var) { 
       if(var%2==0){ 
        try { 
         var.wait(); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
       var++; 
       System.out.println(Thread.currentThread().getName()+" "+var); 
       var.notify(); 
      } 
     } 

    } 
} 

class Odd extends Thread{ 

    Integer var; 

    Odd(Integer var){ 
     this.var=var; 
    } 

    @Override 
    public void run() { 
     while(true){ 
      synchronized (var) { 
       if(var%2!=0){ 
        try { 
         var.wait(); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
       var++; 
       System.out.println(Thread.currentThread().getName()+" "+var); 
       var.notify(); 
      } 
     } 
    } 
} 

Und der Ausgang ist:

OddThread 1

Exception in thread "OddThread" java.lang.IllegalMonitorStateException 

at java.lang.Object.notify(Native Method) 

at mywaitnotifytest.Odd.run(App.java:67) 
+0

Diese Frage scheint anders als OP Ausnahme immer auf 'notify', nicht' wait'. Außerdem ist der Grund der Ausnahme ganz anders und als nichts mit nicht 'synchronisiertem' Code zu tun. – ortis

+0

Sie rufen 'notify() 'nicht auf demselben Objekt auf, wie Sie es gesperrt haben. Kurz gesagt, sperren Sie kein veränderbares Feld. Wenn du es mutierst, änderst du es. Sperren Sie auch nicht ein Pool-Objekt, wie 'Integer' ist, da dies verwirrende Konsequenzen haben wird. –

+0

Ich rufe warte und benachrichtige nur vom synchronisierten Block –

Antwort

3

Ich denke, das auf die übliche Antwort ausreichend unterschiedlich ist ein anderes zu geben.

In diesem Fall Sie synchronized verwenden. Wenn Sie eine Sperre anwenden, handelt es sich bei einem Objekt nicht um eine Referenz.


synchronized (var) { 

Dies sperrt das Objekt var Referenzen, nicht auf var als ein Feld.


var++; 

Dies ersetzt das Objekt var Punkte. Es ist die gleiche wie

var = Integer.valueOf(var.intValue() + 1); 

Hinweis: Integer und in der Tat alle primitiven Wrapper sind Immutable. Wenn Sie eine Operation für sie ausführen, werden Sie tatsächlich aus dem Boxing entfernt, indem Sie den primitiven Wert berechnen und das Objekt neu einboxen. Es ist möglich, das gleiche Objekt zurück zu bekommen, wenn es gepoolt ist. z.B.

Integer i = 10; 
i += 0; // gives back the same object. 

Wenn jedoch das Objekt nicht

gepoolt
Double d = 10; 
d += 0; // creates a new object. 

var.notify(); 

Versuche der Anruf notify auf das neue Objekt, nicht derjenige, der gesperrt wurde.


Sie sollten nicht versuchen, ein Feld zu sperren, das Sie mutieren. Es wird nicht tun, was es zu tun scheint. Sie sollten ein gepooltes Objekt auch nicht sperren. In diesem Fall könnten Sie einen anderen Thread verwenden, der denselben Integer für einen nicht verwandten Zweck verwendet, und notify() wird einen nicht verwandten Thread aufwachen.

zu warten/notify korrekt verwenden, sollten Sie

  • notify() oder notifyAll() nach einer Zustandsänderung in einem anderen gemeinsamen Feld.
  • sollten Sie eine while-Schleife für wait() verwenden, um die Zustandsänderung zu überprüfen.

Wenn Sie dies nicht tun

  • verloren benachrichtigen können, wenn ein anderer Thread wartet nicht.
  • warten kann fälschlich aufwachen, auch wenn keine Benachrichtigung aufgerufen wurde.

Für die oben genannten Anforderungen, was die Bearbeitung im Code vorgeschlagen wird? Wie teile ich dasselbe Objekt für mehrere Threads?

public class PingPong implements Runnable {  
    static class Shared { int num; } 

    private final Shared var; 
    private final int bit; 

    public static void main(String[] args) { 
     Shared var = new Shared(); 
     new Thread(new PingPong(var, 0), "EvenThread").start(); 
     new Thread(new PingPong(var, 1), "OddThread").start(); 
    } 

    PingPong(Shared var, int bit) { 
     this.var = var; 
     this.bit = bit; 
    } 

    @Override 
    public void run() { 
     try { 
      String name = Thread.currentThread().getName(); 
      while (true) { 
       synchronized (var) { 
        while (var.num % 2 == bit) 
         var.wait(); 

        var.num++; 
        System.out.println(name + " " + var.num); 
        var.notify(); 
       } 
      } 
     } catch (InterruptedException e) { 
      System.out.println("Interrupted"); 
     } 
    } 
} 
+2

Deshalb sollten Sie 'final' immer verwenden, wenn Sie mit' synchronized', 'wait' oder' notify' umgehen. – ortis

+0

@ortis wo immer möglich, mit serialisierten Objekten ist es nicht immer möglich, 'final' zu verwenden, aber das Feld sollte sein effektiv endgültig. +1 –

+1

Upvoted diese Antwort. Erklärt, wie die Wartezeit verwendet werden sollte (in einiger Zeit (conditionNotMet) {waitOnTheLockObject;}). Diese Antwort sollte akzeptiert werden. @PeterLawrey, zusammen mit diesem, wenn Sie bearbeiten und explizit erwähnen, dass alle Wrapper-Klassen für Primitive unveränderlich sind, wird es nett sein. Es wird erklären, warum var ++ on Integer ein neues Integer-Objekt (explizit) erstellt. – Amudhan

-1

Statt Integer-Wrapper-Klasse zu verwenden, habe ich meine eigene Klasse und jetzt funktioniert es gut.

package mywaitnotifytest; 

public class App { 
    public static void main(String[] args) { 
     MyInt i = new MyInt(0); 
     Even even = new Even(i); 
     even.setName("EvenThread"); 
     Odd odd = new Odd(i); 
     odd.setName("OddThread"); 
     even.start(); 
     odd.start(); 
    } 
} 

class Even extends Thread { 

    MyInt var; 

    Even(MyInt var) { 
     this.var = var; 
    } 

    @Override 
    public void run() { 
     while (true) { 
      try { 
       Thread.sleep(200); 
      } catch (InterruptedException e1) { 
       // TODO Auto-generated catch block 
       e1.printStackTrace(); 
      } 
      synchronized (var) { 
       if (var.i % 2 == 0) { 
        try { 
         var.wait(); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
       var.i++; 
       System.out.println(Thread.currentThread().getName() + " " + var.i); 
       var.notify(); 
      } 
     } 

    } 

} 

class Odd extends Thread { 
    MyInt var; 

    Odd(MyInt var) { 
     this.var = var; 
    } 

    @Override 
    public void run() { 
     while (true) { 
      try { 
       Thread.sleep(2000); 
      } catch (InterruptedException e1) { 
       // TODO Auto-generated catch block 
       e1.printStackTrace(); 
      } 
      synchronized (var) { 
       if (var.i % 2 != 0) { 
        try { 
         var.wait(); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
       var.i++; 
       System.out.println(Thread.currentThread().getName() + " " + var.i); 
       var.notify(); 

      } 
     } 

    } 
} 

class MyInt { 
    int i = 0; 

    public MyInt(int i) { 
     super(); 
     this.i = i; 
    } 

    @Override 
    public String toString() { 
     // TODO Auto-generated method stub 
     return "" + i; 
    } 

} 

+0

Das Posten einer Problemumgehung, ohne die Ursache des ursprünglichen Problems zu erklären, ist für Leser unbrauchbar. Bitte akzeptiere die obige Antwort. –

+0

Der Grund wird bereits von @Peter Lawrey erklärt. –

Verwandte Themen