Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > de.comp.lang.java > #13039
| Path | csiph.com!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail |
|---|---|
| From | "Christian H. Kuhn" <qno-news@qno.de> |
| Newsgroups | de.comp.lang.java |
| Subject | Re: JUnit-Tests funktionieren nur manchmal: Synchronisations-Problem |
| Date | Fri, 26 Aug 2016 20:21:04 +0200 |
| Lines | 83 |
| Message-ID | <e2bfkkF7jgbU1@mid.individual.net> (permalink) |
| References | <e25pnrFqe4gU1@mid.individual.net> <npmc9e$stq$1@gwaiyur.mb-net.net> |
| Mime-Version | 1.0 |
| Content-Type | text/plain; charset=UTF-8 |
| Content-Transfer-Encoding | 8bit |
| X-Trace | individual.net pu6YhSOCPyoD/47XKnLMrQXiz2IRZXE3OYNTAYhnje5bqsSjk= |
| Cancel-Lock | sha1:pt5t7gA6CC1xwd+4svwGJhzhDBI= |
| User-Agent | Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0 |
| In-Reply-To | <npmc9e$stq$1@gwaiyur.mb-net.net> |
| Xref | csiph.com de.comp.lang.java:13039 |
Show key headers only | View raw
Am 25.08.2016 um 11:03 schrieb Marcel Mueller: > On 24.08.16 16.36, Christian H. Kuhn wrote: > - notified einmalig initialisieren (eigentlich sogar optional), > - jetzt erst alle thread anstupsen, die notified setzen können, > - und dann wait(), > - notified löschen > - und dann das Ereignis, das zu notified gehört, verarbeiten. Ja. Eigentlich selbstverständlich. Mach ich sonst immer so. Hier hab ich dem Oracle-Tutorial geglaubt, ohne selbst zu denken ... > Wie gehören "changed" und "notified" zusammen? Ich habe jetzt keine > Muße, den kompletten Code durchzulesen. Dank dieser Frage fand ich den Fehler. Hoffe ich. Bislang sind keine JUnit-Tests mehr gescheitert. Was eine notwendige, keine hinreichende Bedingung ist, da bin ich inzwischen mißtrauisch ... notified ist die Kontrollvariable in der /Test/-Klasse de.qno.qchessclock.common.QChessClockTest. Nach den von dir angeregten Änderungen wird sie in setUp() gelöscht, in update() gesetzt und in waitForNotify() NACH der Endlosschleife wieder gelöscht. Das ist auch der Sinn: Die Testklasse ist eine Mock-GUI, simuliert Benutzeraktionen und muss nach den Aktionen auf eine Reaktion der zu testenden Klasse warten, bevor die Asserts verarbeitet werden können. changed ist die Kontrollvariable in der /zu testenden/ Klasse de.qno.qchessclock.common.QChessClock. Hier wird alle 20 ms über einen TimerTask notifyObservers() aufgerufen. notifyObservers() erstellt und verschickt nur dann ein Status-Objekt (durch Aufruf von QChessClockObservers.update()), wenn sich der sichtbare Zustand der Uhr verändert hat. changed wird gesetzt: - durch Betätigen eines Knopfes (moveButtonPressed(), stopPressed(), resetPressed()), - durch Setzen einer neuen Bedenkzeit (setNewTimers()), - durch Ändern der Bedenkzeit um mindestens eine Sekunde (wird in notifyObservers() selbst festgestellt), - durch Blättchenfall (wird in notifyObservers() selbst festgestellt). changed wird an einer einzigen Stelle gelösch: - in notifyObservers() (der Methode, die changed auch auswertet) nach Erstellen und Verschicken des Status-Objekts. notified kann also in update() nur gesetzt werden, wenn der Aufruf von update durch gesetztes changed in notifyObservers() erlaubt wird. Dieses Problem war in alten Versionen nicht sichtbar. Dort wurde changed nicht verwendet, update() wurde alle 20 ms aufgerufen. Wurde ein Aufruf verpasst, wirkte der nächste, und der Fehler wäre nur mit Zeitmessungen feststellbar gewesen. Der Fehler war folgender: Ich wollte Resourcen sparen, indem ich den durch synchronized gelockten Code-Abschnitt kurz halten wollte. Insbesondere ist es nicht nötig, die for-Schleife mit den update()-Aufrufen im synchronized-Abschnitt zu haben, da hier nicht mehr auf den Status von QChessClock zugegriffen wird. Das ist auch richtig. Dabei ist mir allerding auch eine Abfrage von changed und das abschließende setChanged(false) aus dem synchronized gerutscht. Dadurch ist eine race condition entstanden. Folgende Event-Reihenfolge wurde möglich: 1. der synchronized-Teil von notifyObservers() wird ausgeführt. changed ist am Ende des Blocks true 2. die for-Schleife mit QChessClockObservers.update() wird begonnen 3. „gleichzeitig“ tritt ein Ereignis ein, das changed wieder auf true setzt 4. die for-Schleife in notifyObservers() wird beendet, changed wird gelöscht. 5. jetzt erst startet der nächste getimte Lauf von notifyObservers(). changed sollte durch die Aktion in 3. true sein, ist aber in 4. ohne Auswertung gelöscht worden. Durch ein paar Zeilentranspositionen kam setChanged(false) in den gelockten Bereich. Dadurch wurde diese race condition vermieden. Snapshot: https://www.qno.de/gitweb/?p=qchessclock.git;a=snapshot;h=304120d97a7ca850851b3c913d344494162b4597;sf=tgz Eclipse, Gradle (lokal) und Jenkins/Gradle (remote) führten die Tests wiederholt ohne Fehler aus. Das gibt Hoffnung. Vielen Dank QNo
Back to de.comp.lang.java | Previous | Next — Previous in thread | Find similar
JUnit-Tests funktionieren nur manchmal: Synchronisations-Problem "Christian H. Kuhn" <qno-news@qno.de> - 2016-08-24 16:36 +0200
Re: JUnit-Tests funktionieren nur manchmal: Synchronisations-Problem Lothar Kimmeringer <news200709@kimmeringer.de> - 2016-08-24 22:30 +0200
Re: JUnit-Tests funktionieren nur manchmal: Synchronisations-Problem "Christian H. Kuhn" <qno-news@qno.de> - 2016-08-26 19:10 +0200
Re: JUnit-Tests funktionieren nur manchmal: Synchronisations-Problem Marcel Mueller <news.5.maazl@spamgourmet.org> - 2016-08-25 11:03 +0200
Re: JUnit-Tests funktionieren nur manchmal: Synchronisations-Problem "Christian H. Kuhn" <qno-news@qno.de> - 2016-08-26 20:21 +0200
csiph-web