2016-04-28 20 views
0

erwartete ich versuche, so etwas zu tun:Synchronisiert und Threads nicht funktioniert als

Es gibt eine Klasse Q die ein Feld n und zwei Methoden put() und get() genannt hat, die den Wert von n-Sets oder ruft den Wert von n. Und dann gibt es zwei Klassen Producer und Consumer. Producer Klasse hat einen Thread, der ruft put und consumer Klasse hat einen Thread, der get ruft. Ich versuche, es zu synchronisieren ein Object lock verwendet, die die einzigen Instanz von Singleton-Klasse ist LockClass.

So Q meine Klasse, hier ist:

public class Q { 

    int n; 

    public void put(int n){ 
     System.out.println("Put " + n); 
     this.n = n; 
    } 

    public int get(){ 
     System.out.println("Got " + n); 
     return n; 
    } 
} 

LockClass:

public class LockClass { 

     private static Object Lock = new Object(); 

     private LockClass(){ 

     } 


     public static Object getLock(){ 
      return Lock; 
     } 
    } 

Verbraucher:

public class Consumer implements Runnable { 

    Thread t; 
    Q q; 


    public Consumer(Q q){ 
     this.q = q; 
     t = new Thread(this); 
     t.start(); 
    } 
    /* (non-Javadoc) 
    * @see java.lang.Runnable#run() 
    */ 
    @Override 
    public void run() { 
     // TODO Auto-generated method stub 
     while(true){ 

      synchronized(LockClass.getLock()){ 
       q.get(); 
      } 
      try { 
       System.out.println("Consumer slept"); 
       Thread.sleep(1000); 
       System.out.println("Consumer awake"); 

      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

} 

Hersteller:

public class Producer implements Runnable { 

    Q q; 
    Thread t; 
    int i; 

    public Producer(Q q){ 
     this.q = q; 
     t = new Thread(this); 
     t.start(); 
     i = 0; 
    } 
    /* (non-Javadoc) 
    * @see java.lang.Runnable#run() 
    */ 
    @Override 
    public void run() { 
     // TODO Auto-generated method stub 
     while(true){ 
      i++; 
      synchronized(LockClass.getLock()){ 
       q.put(i); 
      } 
      try { 
       System.out.println("Producer slept"); 
       Thread.sleep(1000); 
       System.out.println("Producer awake"); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 


} 

DemoClass: Diese Klasse hat die Hauptfunktion

public class DemoClass { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 
     Q q = new Q(); 

     Producer prod = new Producer(q); 
     Consumer cons = new Consumer(q); 
    } 

} 

Also, wenn ich diesen obigen Code ausführen, ich so etwas wie dieses:

Put 1 
Producer slept 
Got 1 
Consumer slept 
Consumer awake 
Producer awake 
Got 1 
Consumer slept 
Put 2 
Producer slept 
Consumer awake 
Producer awake 
Got 2 
Consumer slept 
Put 3 
Producer slept 
Consumer awake 
Producer awake 
Got 3 
Consumer slept 
Put 4 
Producer slept 

Also, ich mache eigentlich die fädeln Sie den Schlaf in Producer und Consumer ein, damit genug Zeit für den Kontextwechsel bleibt. Wenn nun beide gleichzeitig schlafen müssen, wenn der Produzent zuerst schläft, sollte er zuerst wach werden. Aber wie ich in der Ausgabe sehen kann, schläft der Produzent zuerst, aber immer noch erwacht der Verbraucher zuerst und als welcher Thread zuerst erwacht sollte die Sperre erhalten und so ein anderer Thread warten sollte, bis die Sperre freigegeben wird.

Warum funktioniert es nicht so, wie ich es erwarte? Fehle ich etwas?

+1

Jedes Mal, wenn Sie „Erzeuger“ und „Verbraucher“, das erste, was hören Sie ist ein 'BlockingQueue' denken sollte. Der Erzeuger führt Schleifen durch, indem er Objekte in die Warteschlange legt (die Objekte könnten in diesem Fall "Ganzzahl" -Objekte sein), und der Verbraucher führt Schleifen aus, indem er Objekte herausnimmt. Wenn die Warteschlange leer ist, wenn der Benutzer versucht, ein Objekt zu entfernen, wird der Benutzer blockiert, bis ein Objekt verfügbar wird. Und wenn die Warteschlange eine obere Größenbeschränkung hat (oft eine gute Idee), wird der Produzent blockiert, sobald die Warteschlange voll wird. –

+0

@jameslarge: Vielen Dank! :) Ich werde in eine BlockingQueue schauen –

Antwort

1

Es ist nicht garantiert, dass das Sleep-Timeout streng ist. Daher ist es absolut gültig, dass ein Thread später schlafen und früher aufstehen kann. Zitat aus Thread.sleep java doc:

[...] vorbehaltlich der Präzision und Genauigkeit der System-Timer und Disponenten

Theoretisch ist es sogar möglich, dass ein Thread die Aktion zweimal zu machen, während der zweite Thread wird schlafen.

Wenn Sie zwei Threads nacheinander zu handeln, verwenden Warte benachrichtigen Mechanismus:

Produzent

Object lock = LockClass.getLock(); 
synchronized(lock){ 
    q.put(i); 
    lock.notifyAll(); 
    lock.wait();  
} 

Verbraucher

Object lock = LockClass.getLock(); 
synchronized(lock){ 
    q.get(i); 
    lock.notifyAll(); 
    lock.wait();  
} 
+0

Also, ich habe tatsächlich versucht, die gleiche Funktionalität mit dem Single-Lock-Objekt zu verwenden, ohne warten, benachrichtigen und notifyall. Wenn also beide Threads gleichzeitig schlafen und Thread 1 früher als Thread 2 schläft, wird thread2 in der Zwischenzeit schlafen gehen (angenommen, dass er nach x ms schläft). Wenn nun Thread1 aktiviert wird, gibt die JVM dem Thread1 die Kontrolle, da kein anderer Thread vorhanden ist. Währenddessen ist Thread 2 wach und sollte warten, da das Lock von Thread1 genommen wurde. Und das sollte weitergehen. Habe ich ein falsches Verständnis? –

+0

@PriyanshGoel Wenn du Schlaf verwenden willst, versuche eine Lücke zwischen zwei Threads zu machen, zum Beispiel starte den zweiten Thread 500ms später als den ersten Thread – AdamSkywalker

+0

-1, um einen Neuling einzuladen den wait()/notify()/notifyAll zu verwenden() Mechanismus in einer Weise, dass sie nicht verwendet werden sollten. Wait/Notify ist eine wichtige Quelle der Verwirrung für Noobs auf dieser Website. Es ist ein Low-Level-Primitiv, das dazu gedacht ist, [auf sehr spezifische Weise] (https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html) verwendet zu werden, um übergeordnete Synchronisationsobjekte zu implementieren. Die meisten Probleme (einschließlich dieser) können mit den übergeordneten Objekten gelöst werden, die von der Standardbibliothek bereitgestellt werden. (Z. B. "BlockingQueue" würde in diesem Fall gut funktionieren.) –

2

Vom doc:

... Diese Schlafzeiten sind jedoch nicht garantiert, um genau zu sein, weil sie durch die von der zugrunde liegenden OS zur Verfügung gestellt werden ... In jedem Fall können Sie nicht davon ausgehen, dass das Aufrufen von Schlaf den Thread ausgesetzt wird für genau spezifiziert die Zeitperiode

https://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html

+0

Vielen Dank! :) –