2016-06-09 5 views
2

Ich möchte Multithreading (Low-Level-Threading) verwenden, aber ich stehe auf ein Problem. Das Problem ist, dass wegen mindestens warten Methode von einem Thread aufgerufen wird und notifyAll von einem anderen Thread aufgerufen wird, das Problem ist, dass jedes Mal, wenn ich das Programm ausführen, scheint es mir, dass notifyAll vor warten aufgerufen wird, so ich werde ewig warten ".Wie funktioniert die Interaktion mit Threads?

Mein Code ist wie folgt:.

public class Reader extends Thread { 

    Calculator c; 

    public Reader(Calculator cal) { 
     c=cal; 
    } 

    public void run(){ 
     synchronized(c) { 
      try { 
       System.out.println("Waiting for calculation"); 
       c.wait(); 
      } catch (InterruptedException ex) {} 
      System.out.println("Total is:" +c.total);  
     }  
    } 

    public static void main (String[] a) { 

     Calculator calculator=new Calculator(); 

     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
    } 
} 

class Calculator implements Runnable { 

    int total; 

    public void run() { 
     synchronized(this) {  
      for(int i=0;i<100;i++) { 
       total=total+i; 
      }  
      notifyAll();  
     } 
    } 
} 

Was ich hier als Ausgabe 5 Mal in Folge ist für die Berechnung warten, damit ich nie erreichen die Aussage „die Summe ist die Gesamt

I ich versuche es, herauszufinden, wie das Problem zu lösen, aber immer noch nicht eine Lösung zu finden. Wenn jemand eine Ahnung hat, was viel zu tun, wird erkennen, i.

vielen Dank im Voraus

Antwort

1

Sie führen den Rechner nicht tatsächlich runnable aus, der Code, den Sie haben, wird keine Summe berechnen oder irgendetwas mitteilen. Starten Sie einen neuen Thread mit dem Rechner:

new Thread(calculator).start(); 

nach der Zeile, in der Sie die lokale Variable des Rechners initialisieren. Dadurch kann der Rechner arbeiten, während der Hauptthread die Leser startet.

Noch ist es wahrscheinlich, dass der Rechner fertig wird, bevor die Leser beginnen können, zu warten, und die Leser werden am Ende ewig auf eine Benachrichtigung warten, die nie kommen wird. Erstellen Sie eine Bedingungsvariable, in diesem Fall fügen Sie dem Calculator ein boolesches Flag hinzu, das Sie auf false initialisieren und nach der Berechnung auf true setzen (durch notifyAll, bevor Sie den synchronisierten Block verlassen). Haben die Leser in einer Schleife warten:

synchronized(c) { 
    try { 
     System.out.println("Waiting for calculation"); 
     while (!c.finished) { 
      c.wait(); 
     } 
    } catch (InterruptedException ex) {} 
    System.out.println("Total is:" +c.total); 
} 

Auf diese Weise der Rechner im Fall beendet ist, bevor die Leser starten, werden die Leser von dem Bedingungsvariable sagen, dass der Rechner fertig ist, und sie werden nicht warten.

Es ist eine gute Praxis, immer die Warte Methode in einer Schleife aufrufen, da

a), wenn der Thread erhält die Benachrichtigung nicht die Sperre verfügt, in der Zeit zwischen, wenn der Thread benachrichtigt wird und die Zeit es greift die Sperre erneut an Der Status des Systems kann sich aufgrund von Aktionen anderer Threads ändern (in diesem Beispiel nicht der Fall), und

b) Sie können sich nicht auf die Beendigung von wait verlassen, was bedeutet, dass der Thread eine Benachrichtigung erhalten hat .

Mit der oben ändert das Programm endet normalerweise mit dieser Ausgabe:

Waiting for calculation 
Total is:4950 
Waiting for calculation 
Total is:4950 
Waiting for calculation 
Total is:4950 
Waiting for calculation 
Total is:4950 
Waiting for calculation 
Total is:4950 

Hier ist der komplette überarbeitete Code:

public class Reader extends Thread { 

    Calculator c; 

    public Reader(Calculator cal) { 
     c=cal; 
    } 

    public void run(){ 
     synchronized(c) { 
      try { 
       System.out.println("Waiting for calculation"); 
       while (!c.finished) { 
        c.wait(); 
       } 
      } catch (InterruptedException ex) {} 
      System.out.println("Total is:" +c.total);  
     }  
    } 

    public static void main (String[] a) { 

     Calculator calculator=new Calculator(); 
     new Thread(calculator).start(); 

     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
    } 
} 

class Calculator implements Runnable { 

    int total; 
    boolean finished; 

    public void run() { 
     synchronized(this) {  
      finished = false; 
      for(int i=0;i<100;i++) { 
       total=total+i; 
      }  
      notifyAll();  
      finished = true; 
     } 
    } 
} 
+0

gespeichert Sie meinen Tag ein Mann! –

+0

Ich habe nur eine Frage. Mir ist bewusst, dass jeder Thread der Ausführung als Instanz der Klasse Thread beginnt. Wenn ich also die Thread-Klasse erweitern möchte, so wie ich es mit der Reader-Klasse gemacht habe, um einen Thread zu erstellen (also vom neuen Zustand zum runnable-Zustand des Thread-Objekts überzugehen), machen wir etwas wie folgt: new Reader.start(). So, jetzt frage ich mich, warum mein Code nicht eine neue Instanz von Thread für Rechner-Klasse instanziiert ist, wenn ich einfach neue Reader (Rechner) .start(); Es sieht so aus als müsste es funktionieren, aber es funktioniert offensichtlich. Können Sie mir bitte klar, dass –

+0

@ENIO: Sie haben keinen Thread für den Rechner erstellt. Ich bin nicht klar, worüber du verwirrt bist. Wenn Sie ein Runnable als Konstruktor arg an einen neuen Thread übergeben, führt der Thread diesen Runnable aus. Der Reader überschreibt jedoch die run-Methode von Thread und führt nicht zum Ausführen des Calculators. Es ist besser, den Unterklassen-Thread überhaupt zu vermeiden, er verursacht nur Verwirrung. –

0

ich eine Klarstellung würde vorschlagen, was Sie das Endergebnis erwarten Sein. Anscheinend 500? Nicht sicher: P

In jedem Fall empfehle ich die Verwendung von Java Executors. Sie sind Standard-Java-Klassen, die das Ausführen von Threads auf organisierte Weise behandeln.Versuchen Sie folgendes:

public class Example { 
    public static class Calculator implements Runnable { 
     private int result = 0; 

     public void run() { 
      for (int i = 0; i < 100; i++) { 
       result++; 
      } 

      System.out.println("Done. Result = " + result); 
     } 

     public int getResult() { 
      return result; 
     } 
    } 

    public static void main(final String[] args) { 
     final Calculator c = new Calculator(); 
     final ExecutorService executorService = Executors.newSingleThreadExecutor(); 
     executorService.submit(c); 
     executorService.submit(c); 
     executorService.submit(c); 
     executorService.submit(c); 
     executorService.submit(c); 
    } 
} 

Dies gibt mir die folgende:

Done. Result = 100 
Done. Result = 200 
Done. Result = 300 
Done. Result = 400 
Done. Result = 500 

Was dieser Code tun, ist ein einzelner Thread Executor zu schaffen, die Aufgaben ausführen können (Runnable Objekte) und verwendet einen einzelnen Thread dafür. Da es einen einzelnen Thread verwendet, muss jede Task auf die vorherige warten, um zu starten.

Wenn Sie einen Multiple Thread Executor erstellen, wird er versuchen, die Tasks in so vielen Threads auszuführen, und alle folgen demselben Prinzip wie der Single Thread one: für jeden Thread muss ein Task den vorherigen warten es zu starten. Aber mit dem Multiple Thread Executor, wie der Name schon sagt, gibt es mehrere Threads, das bedeutet, dass die Aufgaben gleichzeitig ausgeführt werden und das gleiche Verhalten wie jetzt angezeigt wird.

Hoffe, das hilft!

0

Obwohl Sie über Low-Level-Threading geschrieben haben, ist es klar, dass Ihr Problem mit ForkJoinPool gelöst werden kann. Der Punkt ist, Sie können leicht einen Fehler machen mit wait - notify Anrufe; aber "passiert bevor" Relationen können ohne Aufruf von wait mit der Methode join gesetzt werden.

Voll Code:

import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ForkJoinPool; 
import java.util.concurrent.ForkJoinTask; 
import java.util.concurrent.Future; 
import java.util.logging.Level; 
import java.util.logging.Logger; 

public class Reader extends Thread { 

    final Future<Integer> future; 

    public Reader(Future<Integer> future) { 
     this.future = future; 
    } 

    @Override 
    public void run() { 
     try { 
      int result = future.get(); 
      System.out.println("Total is:" + result); 
     } catch (InterruptedException | ExecutionException ex) { 
      Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    public static void main(String[] a) { 
     Calculator calculator = new Calculator(); 
     ForkJoinPool pool = ForkJoinPool.commonPool(); 
     ForkJoinTask calcTask = pool.submit(calculator); 
     ForkJoinTask[] tasks = { 
      calcTask, 
      pool.submit(new Reader(calcTask)), 
      pool.submit(new Reader(calcTask)), 
      pool.submit(new Reader(calcTask)), 
      pool.submit(new Reader(calcTask)), 
      pool.submit(new Reader(calcTask)) 
     }; 
     //do something else 
     for (ForkJoinTask task : tasks) { 
      task.join(); 
     } 
    } 
} 

class Calculator implements Callable<Integer> { 

    @Override 
    public Integer call() throws Exception { 
     int total = 0; 
     for (int i = 0; i < 100; i++) { 
      total = total + i; 
     } 
     return total; 
    } 
} 
Verwandte Themen