Java Fragen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Hallo, ich habe momentan eine Java Aufgabe aus einer Klausur vorliegen, und hätte gern gewusst, ob ich mit meinen Antworten richtig liege.
    Die Aufgabe ist die folgende:

    Gegeben sei folgende (unvollständige) Klasse:

    Quellcode

    1. public class Verwalter {
    2. private List<Ware> _waren;
    3. public Verwalter() {
    4. _waren = new ArrayList<Ware>();
    5. }
    6. public void preiseAnpassen() {
    7. for (Ware ware : _waren) {
    8. ware.justierePreis();
    9. }
    10. }
    11. }
    Alles anzeigen


    Das Ziel ist es, jede Preisänderung von allen Waren atomar zu gestalten.
    a) Sie finden heraus, dass es so etwas wie Semaphoren mit den Methoden P() und V()
    gibt. Mit welchem Wert müsste die Semaphore initialisiert werden?
    -> Meine Antwort: Mit 1, denn es kann zu Anfang direkt eine Ware justiert werden, danach muss gewartet werden, bis diese fertig ist. Erst dann kann der nächste Preis justiert werden. Es soll ja alles atomar passieren. Richtig/Falsch?
    b) Wie müsste der Quelltext geändert werden, wenn in der Klasse eine Semaphore sem zur
    Verfügung steht?
    -> Meine Antwort: Von der vorherigen Antwort ausgehend:

    Quellcode

    1. public class Verwalter {
    2. private List<Ware> _waren;
    3. public Verwalter() {
    4. _waren = new ArrayList<Ware>();
    5. }
    6. public void preiseAnpassen() {
    7. for (Ware ware : _waren) {
    8. sem.P();
    9. ware.justierePreis();
    10. sem.V();
    11. }
    12. }
    13. }
    Alles anzeigen

    Richtig/Falsch?
    c) Das Programm stürzt direkt ab. Welchen Fehler wurde gemacht, der mit Monitoring nicht
    passiert wäre?
    Das Freigeben/Reservieren wurde vergessen. Dies führt zu einem Deadlock.
    Richtig/Falsch?
    d) Sie finden das Konzept des Monitorings und passen Ihre Klasse Ware wie folgt an

    Quellcode

    1. public class Ware {
    2. ...
    3. public synchronized void justierePreis() {
    4. // Preis wird justiert
    5. }
    6. }

    Führt das zum gewünschten Ergebnis? Wenn nein, was könnte man besser machen?
    -> Meine Antwort: Nein, damit wird die komplette Methode synchroniziert. Wir wollen aber den einzelenen, atomaren Aufruf der Preis justierungs Methode synchronisieren.
    Daher müsste dies in einem synchronized Block geschehen

    Quellcode

    1. synchronized {
    2. ware.justierePreis();
    3. }

    , mit Thread.lock oder mit java.util.concurrent.atomic.
    Richtig/Falsch?

    Würde mich über eine Rückmeldung freuen. Danke vielmals im voraus!
  • a)
    1 ist meiner Meinung nach richtig. In der Java-Praxis würde man für diesen Fall aber eher einen synchronized-Block bzw. eine synchronized-Methode verwenden. Das ist einfacher als eine Semaphore mit dem Wert 1. Sinn machen Semaphoren bei Werten größer als 1. Aber da das eine Verständnisübung ist, sollte 1 richtig sein. Mit deiner Begründung bin ich nicht einverstanden. Das merkst du auch gleich beim Punkt b).

    b)
    Ich denke deine Lösung ist falsch. Die Aufgabenstellung verlangt von dir:
    "jede Preisänderung von allen Waren atomar zu gestalten" (Betonung auf "allen")
    Man möchte also sicherstellen, dass wenn auf verschiedenen Verwalter-Objekten parallel die preiseAnpassen()-Methode aufgerufen wird, sich diese nicht in die Quere kommen.
    Für das was du tun willst, ist die Verwalter-Klasse der falsche Ort. Wenn du sicherstellen willst, dass die justierePreis()-Methode atomar ist, dann ist die Klasse Ware der richtige Ort um dies zu tun. Das ist aber nicht die Aufgabenstellung.

    Quellcode

    1. public class Verwalter {
    2. private List<Ware> _waren;
    3. private final static Semaphor sem = new Semaphor(1);
    4. public Verwalter() {
    5. _waren = new ArrayList<Ware>();
    6. }
    7. public void preiseAnpassen() {
    8. sem.P();
    9. for (Ware ware : _waren) {
    10. ware.justierePreis();
    11. }
    12. sem.V();
    13. }
    14. }
    Alles anzeigen

    Du hast die Dekleration der sem-Variable bei dir weggelassen! Die ist aber wichtig! Besonders das Schlüsselwort "static" ist von entscheidender Bedeutung. Ohne "static" würde es für jedes Verwalter-Objekt eine eigene Semaphore haben und diese wissen nichts voneinander. Du willst aber, dass alle Verwalter-Objekte die gleiche Semaphore verwenden, damit sie sich bei Bedarf blockieren können. Deswegen darf die Semaphore nicht ans Verwalter-Objekt gebunden sein. Stattdessen binden wir sie an die Verwalter-Klasse. Dafür sorgt das "static" Schlüsselwort.
    Nun kannst du auch die Begründung zu a) überarbeiten.
    Zusatzwissen: In Java gibt es die Klasse Semaphore. Diese hat aber keine P()- und V()-Methoden sondern acquire() und release(). Für den Fall, dass du das mal in Wirklichkeit brauchst:
    docs.oracle.com/javase/6/docs/…concurrent/Semaphore.html

    c)
    Hier bin ich mir unsicher, da die Frage schlecht gestellt ist.
    Ich denke, wie du, dass der Aufgabensteller auf den Deadlock hinaus will. Bei einem Deadlock stürzt das Programm aber nicht "direkt ab"... stattdessen friert es ein. Es kann sogar passieren, dass das Programm teilweise noch weiter läuft und nur einige betroffene Threads eingefroren sind.
    Des weiteren verhindert Monitoring keine Fehler, sondern hilft nur die Ursache von Fehlern zu finden.
    Deine Antwort ist jedenfalls richtiger als die Frage. :)

    d)
    Deine Antwort ist falsch. Es geht hier um Monitoring, nicht um das Sperren. Zum Verständnis gehe ich aber erst einmal auf deine Antwort ein, bevor ich mich der Aufgabenstellung widme:
    Ob du der Methode das Schlüsselwort "synchronized" gibst oder den ganzen Inhalt der Methode in einem "synchronized"-Block schachtelst, kommt auf das selbe raus.
    Jedes Objekt in Java kann als Sperrobjekt verwendet werden. Das funktioniert dann im Prinzip wie eine Semaphore die mir 1 initialisiert wurde.

    Quellcode

    1. public synchronized void justierePreis() {
    2. // Preis wird justiert
    3. }

    Quellcode

    1. public void justierePreis() {
    2. synchronized {
    3. // Preis wird justiert
    4. }
    5. }

    Quellcode

    1. public void justierePreis() {
    2. synchronized(this) {
    3. // Preis wird justiert
    4. }
    5. }

    Die obigen drei Beispiele bedeuten alle das Gleiche. Sie legen alle eine Sperre auf das aktuelle Objekt der Klasse Ware. Mann kann die Aufgabe b) auch mittels synchronized anstatt der Semaphore lösen:

    Quellcode

    1. public class Verwalter {
    2. private List<Ware> _waren;
    3. private final static Object statischesSperrObjekt = new Object();
    4. public Verwalter() {
    5. _waren = new ArrayList<Ware>();
    6. }
    7. public void preiseAnpassen() {
    8. synchronized(statischesSperrObjekt) {
    9. for (Ware ware : _waren) {
    10. ware.justierePreis();
    11. }
    12. }
    13. }
    14. }
    Alles anzeigen

    Auch hier wieder die Bedeutung von "static"...
    So, zück zur Aufgabe. Ich weiß nicht was man euch zum Thema Monitorring beigebracht hat. Vielleicht schaust du hier mal in deinen Aufzeichnungen nach, dann ist vielleicht klarer, was gemeint ist. Monitorring bedeutet eigentlich nur, dass man das Programm irgendwie überwachen kann. Im einfachsten Fall ist das eine Ausgabe auf die Konsole:

    Quellcode

    1. public synchronized void justierePreis() {
    2. System.out.println("Preis (" + this + ") wird von Thread " + Thread.currentThread().getId() + " justiert.");
    3. // Preis wird justiert
    4. }

    Wie gesagt, der Begriff Monitorring ist zu abstrakt um hier was konkretes zu implementieren. Der Aufgabensteller kann alles mögliche meinen, schau mal in deinen Aufzeichnungen was bei dem Thema so gemacht wurde.
  • Hafner schrieb:

    a)
    1 ist meiner Meinung nach richtig. In der Java-Praxis würde man für diesen Fall aber eher einen synchronized-Block bzw. eine synchronized-Methode verwenden. Das ist einfacher als eine Semaphore mit dem Wert 1. Sinn machen Semaphoren bei Werten größer als 1. Aber da das eine Verständnisübung ist, sollte 1 richtig sein. Mit deiner Begründung bin ich nicht einverstanden. Das merkst du auch gleich beim Punkt b).

    Ja, würde man für gewöhnlich, aber da dies die Aufgabenstellung ist, nehmen wir eben mal eine Semaphore an. Inwiefern meine Brgündung falsch ist, ist mir jetzt gut klar geworden. Weswegen ist aber dennoch die Initialwert 1 korrekt?

    Hafner schrieb:


    b)
    Ich denke deine Lösung ist falsch. Die Aufgabenstellung verlangt von dir:
    "jede Preisänderung von allen Waren atomar zu gestalten" (Betonung auf "allen")
    Man möchte also sicherstellen, dass wenn auf verschiedenen Verwalter-Objekten parallel die preiseAnpassen()-Methode aufgerufen wird, sich diese nicht in die Quere kommen.
    Für das was du tun willst, ist die Verwalter-Klasse der falsche Ort. Wenn du sicherstellen willst, dass die justierePreis()-Methode atomar ist, dann ist die Klasse Ware der richtige Ort um dies zu tun. Das ist aber nicht die Aufgabenstellung.

    Quellcode

    1. public class Verwalter {
    2. private List _waren;
    3. private final static Semaphor sem = new Semaphor(1);
    4. public Verwalter() {
    5. _waren = new ArrayList();
    6. }
    7. public void preiseAnpassen() {
    8. sem.P();
    9. for (Ware ware : _waren) {
    10. ware.justierePreis();
    11. }
    12. sem.V();
    13. }
    14. }
    Alles anzeigen


    Ah, ok, das hätte ich dann in der Klausur vermutlich völlig falsch gemacht. Danke. Bist du dir dabei jedoch sehr sicher/sicherer als meine Variante/Idee? Atomar ließ mich deuten, dass man dies vor jedem Aufruf machen würde.

    Hafner schrieb:


    Du hast die Dekleration der sem-Variable bei dir weggelassen! Die ist aber wichtig! Besonders das Schlüsselwort "static" ist von entscheidender Bedeutung. Ohne "static" würde es für jedes Verwalter-Objekt eine eigene Semaphore haben und diese wissen nichts voneinander. Du willst aber, dass alle Verwalter-Objekte die gleiche Semaphore verwenden, damit sie sich bei Bedarf blockieren können. Deswegen darf die Semaphore nicht ans Verwalter-Objekt gebunden sein. Stattdessen binden wir sie an die Verwalter-Klasse. Dafür sorgt das "static" Schlüsselwort.

    Das weiß ich, aber da es nur geschriebener Quellcode ist (Klausur eben) denke ich, ist das weglassen kein Drama. Aber danke für die Aufmerksamkeit.

    Hafner schrieb:


    Nun kannst du auch die Begründung zu a) überarbeiten.
    Zusatzwissen: In Java gibt es die Klasse Semaphore. Diese hat aber keine P()- und V()-Methoden sondern acquire() und release(). Für den Fall, dass du das mal in Wirklichkeit brauchst:
    docs.oracle.com/javase/6/docs/…concurrent/Semaphore.html

    Danke, das wusste ich noch nicht, das ist gutes Grundwissen.

    Hafner schrieb:


    c)
    Hier bin ich mir unsicher, da die Frage schlecht gestellt ist.
    Ich denke, wie du, dass der Aufgabensteller auf den Deadlock hinaus will. Bei einem Deadlock stürzt das Programm aber nicht "direkt ab"... stattdessen friert es ein. Es kann sogar passieren, dass das Programm teilweise noch weiter läuft und nur einige betroffene Threads eingefroren sind.
    Des weiteren verhindert Monitoring keine Fehler, sondern hilft nur die Ursache von Fehlern zu finden.
    Deine Antwort ist jedenfalls richtiger als die Frage. :)

    Ja die Frage ist wirklich absolut hinrissig gestellt, gemeinhin könnte das viele Gründe haben, aber ich denke auch sie meinen den Deadlock durch vergessenes Freigeben/Sperren (wobei: eher nur das vergessene Freigeben, oder?)

    Hafner schrieb:


    d)
    Deine Antwort ist falsch. Es geht hier um Monitoring, nicht um das Sperren. Zum Verständnis gehe ich aber erst einmal auf deine Antwort ein, bevor ich mich der Aufgabenstellung widme:
    Ob du der Methode das Schlüsselwort "synchronized" gibst oder den ganzen Inhalt der Methode in einem "synchronized"-Block schachtelst, kommt auf das selbe raus.
    Jedes Objekt in Java kann als Sperrobjekt verwendet werden. Das funktioniert dann im Prinzip wie eine Semaphore die mir 1 initialisiert wurde.

    Quellcode

    1. public synchronized void justierePreis() {
    2. // Preis wird justiert
    3. }

    Quellcode

    1. public void justierePreis() {
    2. synchronized {
    3. // Preis wird justiert
    4. }
    5. }

    Quellcode

    1. public void justierePreis() {
    2. synchronized(this) {
    3. // Preis wird justiert
    4. }
    5. }

    Die obigen drei Beispiele bedeuten alle das Gleiche. Sie legen alle eine Sperre auf das aktuelle Objekt der Klasse Ware. Mann kann die Aufgabe b) auch mittels synchronized anstatt der Semaphore lösen:

    Quellcode

    1. public class Verwalter {
    2. private List _waren;
    3. private final static Object statischesSperrObjekt = new Object();
    4. public Verwalter() {
    5. _waren = new ArrayList();
    6. }
    7. public void preiseAnpassen() {
    8. synchronized(statischesSperrObjekt) {
    9. for (Ware ware : _waren) {
    10. ware.justierePreis();
    11. }
    12. }
    13. }
    14. }
    Alles anzeigen

    Auch hier wieder die Bedeutung von "static"...
    So, zück zur Aufgabe. Ich weiß nicht was man euch zum Thema Monitorring beigebracht hat. Vielleicht schaust du hier mal in deinen Aufzeichnungen nach, dann ist vielleicht klarer, was gemeint ist. Monitorring bedeutet eigentlich nur, dass man das Programm irgendwie überwachen kann. Im einfachsten Fall ist das eine Ausgabe auf die Konsole:

    Quellcode

    1. public synchronized void justierePreis() {
    2. System.out.println("Preis (" + this + ") wird von Thread " + Thread.currentThread().getId() + " justiert.");
    3. // Preis wird justiert
    4. }

    Wie gesagt, der Begriff Monitorring ist zu abstrakt um hier was konkretes zu implementieren. Der Aufgabensteller kann alles mögliche meinen, schau mal in deinen Aufzeichnungen was bei dem Thema so gemacht wurde.

    Also mit anderen Worten: Ja das führt zum gewünschten Ergebnis, es müsste nichts mehr getan werden, oder?
    Ich war mir dabei absolut unsicher deswegen hierfür vielen Dank für die Ausführlichkeit.
  • Ja, würde man für gewöhnlich, aber da dies die Aufgabenstellung ist, nehmen wir eben mal eine Semaphore an. Inwiefern meine Brgündung falsch ist, ist mir jetzt gut klar geworden. Weswegen ist aber dennoch die Initialwert 1 korrekt?

    Na du willst doch, dass nur ein Verwalter gleichzeitig den Inhalt der Methode preiseAnpassen() ausführen kann. Das habe ich jetzt mal unter "atomar" verstanden. Wenn zwei Verwalter gleichzeitig den Methodeninhalt hätten ausführen sollen, dann hätte man die Semaphore mit 2 initialisiert.

    Ah, ok, das hätte ich dann in der Klausur vermutlich völlig falsch gemacht. Danke. Bist du dir dabei jedoch sehr sicher/sicherer als meine Variante/Idee? Atomar ließ mich deuten, dass man dies vor jedem Aufruf machen würde.

    Naja, die menschliche Sprache lässt immer etwas Spielraum für Interpretation. Allerdings macht es in der for-Schleife einfach keinen Sinn, zumal (wie du bei Beim Aufgabenteil d sehen kannst) die Methode justierePreis() bereits synchronized ist. Deswegen würde es mich eher überraschen wenn ich hier falsch liege.

    Quellcode

    1. Ja die Frage ist wirklich absolut hinrissig gestellt, gemeinhin könnte das viele Gründe haben, aber ich denke auch sie meinen den Deadlock durch vergessenes Freigeben/Sperren (wobei: eher nur das vergessene Freigeben, oder?)

    Richtig, nur das vergessene Freigeben sollte zu einen Deadlock führen. Vergessenes Sperren kann zu Race Conditions führen (genau wie überhaupt keine Sperren zu verwenden). Das verursacht aber in der Regel nicht Programmabstürze sondern einfach nur falsche Werte.

    Also mit anderen Worten: Ja das führt zum gewünschten Ergebnis, es müsste nichts mehr getan werden, oder?

    Ist für mich schwer nachzuvollziehen was der Aufgabensteller will.
    Wenn ich z.B. die eine Java-Anwendung monitoren will, dann mache ich eigentlich nichts am Quellcode. Stattdessen starte ich ein Tool wie jvisualvm (bei jeder JDK-Installation mit dabei). Dort kann ich für jede laufende Java-Anwendung die Threads beobachten. Das ist für mich Monitorring. Stattdessen wird einem hier ein Stück Quellcode vorgesetzt und gesagt "Sie finden das Konzept des Monitorrings". Da finde ich überhaupt kein Monitorring... Ich kann nur raten, dass der Aufgabensteller dort irgend eine Ausgabe haben möchte (da es ja offensichtlich im Quellcode stehen muss). Wie gesagt, vielleicht wird sich hier auf etwas spezielles bezogen, dass ihr gelernt habt. Ich als Außenstehender versehe die Frage nicht.
  • OK, ich hab unter Monitoring das hier verstanden:
    de.wikipedia.org/wiki/Monitoring
    Der Begriff ist wohl einfach doppelt belegt.

    Die Bezeichnung Monitor für synchronized Blöcke/Methoden war mir nicht geläufig. Naja, man lernt nie aus. :) Dann ergeben die Aufgaben natürlich viel mehr Sinn.


    Hmm, jetzt verstehe ich d). Also war diese justierePreis()-Methode noch nicht synchronized in Aufgabe b) sondern das ist ein nur ein Änderungsvorschlag. Trotzdem bleibt die preiseAnpassen()-Methode der falsche Ort um die justierePreis()-Methode vor parallelen Zugriff zu sichern. Nun bin ich mir nicht mehr sicher. Vielleicht ist b) und die Begründung von a) doch richtig. Vielleicht auch nicht. :)
    "Das Ziel ist es, jede Preisänderung von allen Waren atomar zu gestalten." --> Wenn er das gemeint hat, ist deine Lösung richtig.
    "Das Ziel ist es, jede Preisänderung von allen Waren eines Verwalters atomar zu gestalten." --> Wenn er das gemeint hat, ist würde ich eher zu meiner Lösung tendieren.
    Insgesamt neige ich trotzdem noch zu meinem Lösungsvorschlag.

    Bei c) bleibt die Aussage eigentlich richtig (abgesehen von dem Detail mit dem Deadlock beim freigeben). "synchronized" kümmert sich sozusagen automatisch um das Freigeben. Das schützt dich aber nicht vor Deadlocks, sondern verringert "nur" diese Fehlerquelle.


    Bei d) ist das Konzept des Monitoring natürlich dann das "synchronized"-Schlüsselwort.
    Wie die Antwort hier lautet hängt unter anderem stark davon ab was bei b) richtig ist.
    Zunächst muss man sich bewusst sein, dass "synchronized" hier eine Sperre an das Objekt hängt. Das bedeutet, wenn du zwei Instanzen von Ware hast, dann bocken diese sich nicht gegenseitig. Die Sperre greift also nur, wenn du eine Instanz von Ware mehreren Verwaltern übergibst (vergleiche mit statischer Deklaration der Semaphore). Dafür ist die Methode auch vor Parallelität geschützt, wenn es Zugriffe darauf gibt, die nicht aus der preiseAnpassen()-Methode kommen.
    Wenn mein Lösungsvorschlag bei b) stimmt, ist das eh alles egal, da man nicht den Einzelaufruf der justierePreis()-Methode schützen will, sondern die gesammte for-Schleife in der preiseAnpassen()-Methode.