Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.
Naja, wenn ich java -version eingebe kommt:
java version “1.7.0_21”
Java™ SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot™ 64-Bit Server VM (build 23.21-b01, mixed mode)
und bei javac -Version eingebe kommt:
javac 1.7.0_21
Es gibt mehrere Alternativen für die Codezeile oben: klickEntweder du benutzt [m]Array.newInstance(Class<?>, int)[/m] () - oder einfach so:
Das müsste es auch funktionieren… (ungetestet)
workers = (FixedThreadCompletionService<V>.Worker) new FixedThreadCompletionService.Worker[threads];
Eventuell kannst du dann noch die Compiler-Warnung zu dem Cast mit einem [m]@SuppressWarnings({„unchecked“})[/m] unterdrücken.
Falls du auf jeden Fall Typsicherheit möchtest, musst du statt dem Array zwingend Collections verwenden, beispielsweise eine ArrayList:
workers = new ArrayList<FixedThreadCompletionService<V>.Worker>();
Ich komme bei der Implementierung der [m]shutdown()[/m]-Funktionalität gerade nicht wirklich voran… Könnte mir jemand mal einen Tipp geben, was ich bei [m]take()[/m] beachten muss, um nicht andauernd [m]null[/m] zurückzugeben ([m]takeReturn[/m] aus dem Testcase ist immer [m]null[/m]), wenn [m]shutdown [/m]gesetzt wurde? Daran scheitern nämlich gerade bei mir (fast) alle Testcases…
du redest also von der zweiten b) ?
Du machst da eigentlich nicht viel anders, als bei der shutdown-implementierung, nur eben nicht mit shutdown, sondern mit terminated. Zusätzlich noch eine Kleinigkeit beachten, auf die man aber sofort kommt, sobald man das Prinzip verstanden hat.
Wenn der Hinweis ungenügend sein sollte → nochmal nachfragen!
Ich glaube, das Problem ist meine “Abbruchbedingung” in [m]take()[/m], bzw. wann [m]take()[/m] [m]null[/m] liefern darf und wann nicht. Momentan rufe ich poll() so lange auf, bis ich ein Element bekommen habe oder eben [m]terminated == true[/m], und gebe das Element bzw. [m]null[/m] zurück, wenn ich entweder eine poisonpill bekommen habe oder das Element eben selbst [m]null[/m] ist (so stand es ja auch in der Angabe).
Dabei kommt dann aber jedes mal die Ausgabe “take returned null”. Wenn ich jedes mal das Element zurückgebe, schlagen die Tests natürlich auch fehl.
Ok, dann war das schonmal falsch… Aber das ändert am Problem mit[m] take()[/m] leider noch nichts.
Muss ich denn die Anzahl der Threads, die ich in der[m] take()[/m]-Methode zähle, auch wieder mit jeder genommenen poisonpill wieder runterzählen? Wie gesagt, mir fehlt irgendwie noch die richtige Abbruchbedingung bzw. das richtige Timing dafür.
Soll unser take() auch warten, wenn der head null ist wie beim normalen CompletionService auch? Steht ja nichts davon in der Aufgabe & wenn ichs mache, dann haengt sich der Testcase beim 1. take sofort auf … ohne des Warten schlagen dennoch 3/4 Testcases fehl :-/
Das take() soll so lange blockieren, bis das naechste Arbeitspaket fertig ist.
Wenn du shutdown() aufgerufen hast, dann musst du sicherstellen, dass die
takes(), die kein fertiges Arbeitspaket mehr kriegen werden, eben nicht ewig
blockieren, sondern irgendwann mit null zurueckkehren. Und genau da sollt ihr
euch eben etwas Schlaues ueberlegen, wie man das hinkriegt.
Das hab ich meiner Meinung nach eig. auch gemacht - kurz bevor ich los musste hab ich dem Tutor in der Rechnerübung auch gesagt wie ichs machen will (teilweise schon implementiert, Rest war zu dem Zeitpunkt noch eher Pseudo-Code) und er hat gesagt des passt so …
Kannst du etwas genauer beschreiben, was du in take() und terminationHandler()
machst? Ohne weiteren Anhaltspunkt kann ich nicht sagen, was der Fehler sein
koennte.
Gut, das lässt sich ja einfach genau so implementieren.
Aber bei mir läufts dann darauf raus, dass die Threads irgendwann alle an der Schlange stehen und warten, und sich genau dann beenden, wenn das FixedThreadCompletion Objekt stirbt.
Das war kein Problem, so lange es nur um Teilaufgaben a-d ging. Aber danach wirds finster…
Weil da soll man ja den Zustand „nach Beenden des letzten Threads“ beachten… und den erreiche ich so ja nie.
wäre also die eigentlich richtige Aufgabenstellung in der c) nicht eher:
Mhm, meine Antwort kommt inzwischen wohl etwas zu spät - naja mal schauen, ob du noch da bist … denke aber mal eher nicht
Im handler setz ich das entsprechende Flag, dekrementiere meinen Counter vom take (der wird natürlich immer, wenn ein Thread in take kommt inkrementiert) & lege eine poisonpill in die queue der fertigen Prozesse.
Falls du das heute nicht mehr liest, brauchst du dir keine Mühe geben … werde ja dann in der Korrektur lesen woran es lag - danke schonmal, so oder so.
EDIT: In take head auf poisonpill überprüfen (ggf. entfernen und null returnen), counter (den oben genannten für den handler) erhöhen, warten, falls queue der fertigen Prozesse leer, wenn entsprechendes Flag gesetzt aus take raus.
Deine Worker-Threads holen sich in einer Schleife immer wieder Arbeitspakete
aus der Input-Queue.
Jetzt kommt die Abbruchbedingung fuer deine Schleife:
Sobald du shutdown() aufgerufen hast, koennen keine neuen Arbeitspakete
mehr nachkommen. d.h., irgendwann ist die Input-Queue leer und ein
Worker-Thread nach dem anderen soll sich beenden, weil es nichts mehr
zu tun gibt.
Da deine Worker-Threads sich nicht zum exakt gleichen Zeitpunkt beenden,
wird es einen Thread geben, der sich als letzter beendet hat.
Und der ruft den terminationHandler auf.
Nein. Aufgaben a-d) beziehen sich auf die Implementierung des „reinen“ CompletionService
ohne die Shutdown-Funktionalitaet. Da warten die Threads ewig und sobald ein
neues Paket ankommt, bearbeiten sie es.
Vieleicht hilfts ja sonst noch jemandem
Warum dekrementierst du deinen Zaehler im terminationHandler()?
Nur das take() soll mitzaehlen, wie viele Aufrufer sich gerade in der Methode
befinden. „Mitzaehlen“ heisst aber beim Betreten inkrementieren und
beim Verlassen dekrementieren
Weil take() und terminationHandler() auf das terminated-Flag, die Queue und den
Zaehler zugreifen, kanns Race-Conditions geben und man muss peinlich genau
auf die Reihenfolge der Zugriffe achten, damit der Code funktioniert.
Das ist der Knackpunkt der Aufgabe
Wenn du den Counter z.B. an der falschen Stelle im take() inkrementierst,
kann es passieren, dass der terminationHandler dir eine poisonpill zu wenig in die Queue
steckt und dein take() danach ewig blockiert, weil keine fertigen Arbeitspakete mehr reinkommen.