PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Reaktionstest in JAVA


Gast
2004-12-25, 16:15:18
Hallo und erstmal fröhliche Weihnachten!
Da ich gerade nichts zu tun habe und ich noch paar Aufgaben von der Uni machen wollte,habe ich folgendes Problem:Erstmal Aufgabe:

Ein TestGenerator erzeugt in wählbaren Zeitabständen zwei numerische Werte aus dem Bereich 1 bis 3, die im Feld „Test“ angezeigt werden. Dabei sollte die Anzeige neuer Testwerte auch dann deutlich erkennbar sein, wenn gleiche Testwerte aufeinanderfolgen.
Der Sleepwert gibt an nach wie viel ms neue Testwerte erzeugt werden. Mit den Button
„Sleep–“ und „Sleep+“ kann der Sleepwert jeweils um 20 % verringert bzw. erhöht werden.
Der Spieler muss immer dann den Button Reaktion drücken, wenn die beiden Zahlen gleich sind. Gelingt ihm dies, erhöht sich der erste Wert im Ergebnisfeld; versäumt er es, wird der zweite Wert erhöht. – Der zweite Wert wird auch dann erhöht, wenn der Reaktion Button gedrückt wird, obwohl die beiden Testwerte ungleich sind.
Das Ergebnisfeld zeigt also das Verhältnis der richtigen zu den falschen Reaktionen.
Die Aufgabe ist mit zwei synchronisierten Threads zu lösen:
• einen, der die Testfälle erzeugt und sie samt der Reaktion des Benutzers speichert
• einen, der die Reaktion des Spielers verarbeitet

Nun,das ist alles nicht so schwer bekomme ich auch hin.Ich habe nur das Problem,dass der Mausklick auf dem Button einfach zu langsam ist.Wenn ich drücke bei zwei richtigen Werten,dann erhöht sich bei mir auch der zweite Wert(die falsche Reaktion).Es scheint so,als,ob immer bei zwei richtigen Werten,wenn man nicht geklickt hat,schneller in derAuswertung ist!
Was kann ich da mchen?

Senior Sanchez
2004-12-25, 18:53:27
kannste mal den quelltext posten?

Nicht dass de da nen kleinen fehler im quelltext hast.


mfg Senior Sanchez

SGT.Hawk
2004-12-25, 19:02:07
Ok,habe ich.Kann es sein,dass ich vieleicht die Werte synchronized machen muss?Na,ja,schaut es euch selbst an.

Senior Sanchez
2004-12-25, 19:16:07
Auf die schnelle konnte ich da erstmal keinen fehler sehen........ versuche es mal synchronized.

BTW: deine Zufallszahlenerzeugung ist nicht richtig, ich denke es soll von 1 bis 3 und nicht von 0-4 sein ;)


mfg Senior Sanchez

SGT.Hawk
2004-12-25, 19:25:11
Oh.Danke für den Tip.
JA,ich weiss auch nicht warum,hast du mal getestet,was ich meine?
Oder liegt das einfach an SWING,dass es zu träge ist und die Eventbehandlung nicht schnell genug hinbekommt.Man kann ja Threads nicht debuggen)-;

Senior Sanchez
2004-12-25, 19:34:18
Nee, habe ich nicht getestet, da mir dazu glaube auch irgendwelche libraries fehlen und ich zu faul bin, die mir zu besorgen ;)

Ich weiß aber was du meinst: Du drückst auf den Button, das zwei gleiche Nummern gezeigt werden. Indem Moment zählt dann die win-Zahl hoch, aber irgendwie fuscht da noch nen thread rein, der die loss zahl erhöht.

Hmm, swing ist ja ansich net thread-sicher, das macht schon nen paar probs im umgang mit threads. Was mir aufgefallen ist, dass du die threads nach nem bestimmten zeitintervall das gegeben ist immer schlafen legst, aber kann es da nicht zu zeitunterschieden kommen, sodass die zu verschiedenen Zeiten schlafen? Kannste die da nich irgendwie zusammenschalten oder so?

Probiere mal lange schlafzeiten zu wählen ob der Fehler dann immernoch auftritt. Wenn nicht, wird das ganze was mit den threads und dem timing zu tun haben.

Könnteste mir ne zip-datei schnüren wo alles dabei ist? Dann kann ich das hier mal testen und dir besser helfen.

mfg Senior Sanchez

SGT.Hawk
2004-12-25, 19:43:39
JA,genau!!!!Aleerdings wird bei mir zuerst der Loss wert erhöht und dann dann der Win Wert.Du müsst sie einfach so ausführen können,wenn du sie in eine Projekt einbindest.Habe sie dir geschickt!Tja,mir gehen die Ideen aus.JA mit den Sleep werten habe ich auch schon rumgespielt,aber sie scheinen sich gleichmässig abzuwechseln,habe einfach println gemacht,um zusehen.Hm,also aus der Lektüre habe ich gelesen,dass SWING nicht thread-safe ist!
Ich kann doch keine zip verschicken...

Senior Sanchez
2004-12-25, 20:35:43
Also ich meine eine Fehlerquelle gefunden zu haben:

Der Thread, der die loss hochzählt ist zu scharf getimed. Sind nämlich zwei zahlen gleich, wird sofort die loss variable inkrementiert, ohne darauf zu warten wie der nutzer reagiert. Du dürftest also eigentlich erst inkrementieren wenn die zahlen wechseln, und der nutzer nicht auf den button gedrückt hat. Da hat sich nämlich aus diesesm fehlenden handling noch nen fehler ergeben: sind zwei zahlen gleich kann man ganz schnell 10 mal oder so drücken und seine punktzahl hochtreiben. ich weiß nicht ob das der sinn der sache ist ;)


mfg Senior Sanchez

HellHorse
2004-12-25, 23:49:55
dein Code:

class ReactionL implements ActionListener{
public void actionPerformed(ActionEvent evt){
clicked=true;
JTextField jf= rt.getJtResult();
if(rn.getNumber1()==rn.getNumber2())
jf.setText(win++ +" : "+loss);
else
jf.setText(win+" : "+loss++);
}
}

public void run(){
JTextField jf= rt.getJtResult();
while(!isInterrupted()){
if(rn.getNumber1()==rn.getNumber2() && !clicked )
jf.setText(win+" : "+loss++);
clicked=false;
try{
Thread.sleep(Integer.parseInt(rt.getJtSleep().getText()));
}
catch(InterruptedException e){
interrupt();
}
}

Auf deutsch:
Wenn jemand click wird loss oder win raufgesetz.
Periodisch wird überprüft ob die beiden Werte überinstimmen. Falls ja und seit dem letzten Check nicht click gedrückt wurde, dann wird loss raufgesetzt. Daneben werden davon unabhängig Nummern generiert.
Siehe auch, was Sanchez geschrieben hat.

Wie es imo aussehen könnte:
- Der Generatorthread generiert Nummern. Wenn er eine neue generiert und seit dem letzten mal noch nicht geclickt wurde, inkrementiert er loss. Dadruch bist du die Timingprobleme los.
- Der Verarbeitungsthread wartet. Wenn jemand einen Knopf drück wacht er auf und vergeleicht die beiden Werte und setzt irgend einen AtomicBoolean für den GeneratorThread, dass geclickt wurde. Je nach Wortlaut der Aufgabenstellung kann man das sogar im EventDispatchThread machen.

Wegen der Beobachtung von Sanchez:
Sobald erfolgreich geclickt, "Reaction"-Button disablen und Generatorthread aufwecken.

Wenn das Teil läuft, sollte "Start" natürlich disabled sein.

Und was erhoffst du dir von solchen Code?

catch(InterruptedException e){
interrupt();
}

Und was soll das? Das ist ja wohl nicht dein Ernst, oder? Wenn du das system default LAF willst, dann UIManager.getSystemLookAndFeelClassName().

UIManager.LookAndFeelInfo[] lafArr=
UIManager.getInstalledLookAndFeels();
try {
UIManager.setLookAndFeel(lafArr[2].getClassName());
} catch (Exception e) {}


Postinkrement auf int's ist nicht atomar und win und loss werden in zwei unabhängigen Threads bearbeitet. Ebenso solltest du clicked zumindest volatile machen.
Und GUI-Komponenten gefälligst nur im EventDispatchThread modifizieren.

Senior Sanchez
2004-12-26, 00:55:19
clicked ist btw auf volatile, schon die ganze zeit ;)

aber zu der anderen sache:

Und was erhoffst du dir von solchen Code?


catch(InterruptedException e){ interrupt(); }


Das muss so gehandhabt werden. Schläft ein Thread und es wird ein interrupt() auf ihm aufgerufen, so wird intern ne variable gesetzt, die sagt, dass er beendet werden soll und gleichzeitig eine InterruptedException geworfen. Dies unterbricht aber den schlafvorgang, weckt den thread auf und kurioserweise wird intern die variable wieder zurückgesetzt, dass er noch laufen soll und nicht interrupted wurde. Also muss er nochmal auf interrupt gesetzt werden ;)


mfg Senior Sanchez

SGT.Hawk
2004-12-26, 01:09:11
clicked ist btw auf volatile, schon die ganze zeit ;)

aber zu der anderen sache:

Und was erhoffst du dir von solchen Code?


catch(InterruptedException e){ interrupt(); }


Das muss so gehandhabt werden. Schläft ein Thread und es wird ein interrupt() auf ihm aufgerufen, so wird intern ne variable gesetzt, die sagt, dass er beendet werden soll. Dies unterbricht aber den schlafvorgang, weckt den thread auf und kurioserweise wird intern die variable wieder zurückgesetzt, dass er noch laufen soll und nicht interrupted wurde. Also muss er nochmal auf interrupt gesetzt werden ;)


mfg Senior Sanchez

Ja,genau besser kann man es nicht erklären,denn so habe ich gelernt,dass man Threads sicher beenden kann wenn man will.
@Hellhorse
mit dem UIManager:ich benutze das Windows Look and Feel deswegen.
Mit volatile habe ich gelesen,dass man es braucht,wenn alle Threads die Variable aktuell jhaben sollten,wenn man nicht synchronisiert.
Ja,ich weiss dass Inkre/Decr nicht atomar sind,aber die werden doch von einem anderen Thread doch gar nicht zugegriffen!
OK,wenn ich im GeneratorThread natürlich auf clicked zugreife,dann muss ich doch das syncronized machen oder?

Senior Sanchez
2004-12-26, 01:23:01
Mit volatile habe ich gelesen,dass man es braucht,wenn alle Threads die Variable aktuell jhaben sollten,wenn man nicht synchronisiert.


Das stimmt in etwa so. Volatile sagt aus, dass der Zugriff atomar erfolgt und so verhindert wird das zwei Threads gleichzeitig in die Variable schreiben und am Ende nur bullshit rauskommt.
Synchronized benutzt man halt um komplette methoden oder blöcke atomar ablaufen zu lassen.


mfg Senior Sanchez

HellHorse
2004-12-26, 04:06:46
Das muss so gehandhabt werden. Schläft ein Thread und es wird ein interrupt() auf ihm aufgerufen, so wird intern ne variable gesetzt, die sagt, dass er beendet werden soll und gleichzeitig eine InterruptedException geworfen. Dies unterbricht aber den schlafvorgang, weckt den thread auf und kurioserweise wird intern die variable wieder zurückgesetzt, dass er noch laufen soll und nicht interrupted wurde. Also muss er nochmal auf interrupt gesetzt werden ;)
Und warum reicht genau ein simples break oder return nicht?

mit dem UIManager:ich benutze das Windows Look and Feel deswegen.

Und wo steht geschrieben, dass das dritte installierte LAF das Windows LAF ist?

Volatile sagt aus, dass der Zugriff atomar erfolgt und so verhindert wird das zwei Threads gleichzeitig in die Variable schreiben und am Ende nur bullshit rauskommt.

Volatile verhindert dass ein Thread eine lokale Kopie einer Variable hat. Load und Store sind immer atomar (ausser bei long und double), der Rest nie. Hier spielt genauso wie bei synchronized aber noch das Java Memory Model rein.

Ja,ich weiss dass Inkre/Decr nicht atomar sind,aber die werden doch von einem anderen Thread doch gar nicht zugegriffen!
Doch EventDispatch- und Generatorthread.

OK,wenn ich im GeneratorThread natürlich auf clicked zugreife,dann muss ich doch das
syncronized machen oder?
Du kannst variablen nicht synchronized machen. java.util.concurrent.atomic.AtomicInteger oder ein ein Lock Objekt auf das du synchronisierst.

Senior Sanchez
2004-12-26, 13:31:15
Stimmt, an der stelle würde auch nen einfaches break oder return reichen. Aber im Endeffekt ist es eh latte, ob de da jetzt interrupt() oder nen break/return benutzt. Aus der schleife führen alle drei.


mfg Senior Sanchez

SGT.Hawk
2004-12-26, 15:55:01
Bin gerade nicht zu Hause,um mein Programm zu ändern.Wenn ich das getab habe,melde ich mich wieder.
Der Fokus liegt wohl bei clicked,denke mal der muss synchronized sein.
Wie steht es mit getNumber(),da die auch von Verabeiterthread aufgerufen wird muss die doch auch syncronized sein,meiner Meinung.

SGT.Hawk
2004-12-27, 18:59:02
So,habe jetzt´eine funktionierende Lösung,allerdings bin ich so vom Design nicht so begeistert,aber ich bitte um Verbesserungsvorschläge,seien sie auch nur so klein.Habe,dass jetzt mit wait() und notify() gemacht und so wie es ihr mir vorgeschalgen habt.
@Habe mich schon gewundert,warum sich keiner meldet,da die Files ja noch fehlten.

SGT.Hawk
2005-01-01, 21:01:23
So jetzt ist alles vollständig!

HellHorse
2005-01-02, 12:32:44
Positives/Verbesserungen:

auf den ersten Blick funktioniert es
inkement von win und loss thread-safe
Timing-Probleme gelöst


Negatives (solltest du fixen, bevor du es abgibst):

auf den zweiten Blick funktioniert es nicht immer
setze die Rektionszeit sehr tief (um die 20ms) und jede Menge lustiger Exceptions kommen. Ein Problem ist dass du uU die ArrayList noch modifizierst, während du schon darüber iterierst. ArrayList merkt das, und hat wenig Freude daran.
du modifizierst Swing-Komponenten in mehreren Threads, manche davon ausserhalb des Event-Dispatch-Threads, das ist ganz pöse und gefährlich

http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
http://java.sun.com/products/jfc/tsc/articles/threads/threads3.html

einmal war der String ein einem Textfeld vermutlich als Folge davon "", was zu einer NumberFormatException führte


Unschönes (kannst du fixen, wenn du Zeit und Lust hast):

java.util.Random#nextInt(3) + 1 macht auch was du willst etwas einfacher
ReactionSolver braucht nicht mehr von Thread zu erben und auch keine run Methode mehr
verwende Frame#pack statt setSize
verwende die Konstanten von BorderLayout, statt deren Werte
wenn du wirklich das WindowsLAF willst, der Klassenname ist "com.sun.java.swing.plaf.windows.WindowsLookAndFeel", verwende den oder besser direkt UIManager.getSystemLookAndFeelClassName(). Deine jetzige Methode ist Glücksspiel. Und fange die Exceptions einzeln ab. } catch (Exception e) {
} Sowas ist kein guter Programmierstil. Kommentiere es zumindest.
ich würde von der Verwendung von lokalen Klassen absehen, besonders wenn der eigentliche Code mehr als eine Zeile ist und sie bloss einmal instantiert werden. Sie verwirren bloss und machen die Methode unnötig gross.
das ganze Layout liesse sich einfacher mir GridLayout machen. Allerdings werden dann beim Resizen die Komponenten einfach grösser. #setResizeable(false) ist also eine Überlegung wert.
du verwendest zwar ArrayList, was gut ist, programmierst aber gegen die Klasse und nicht das Interface List, was weniger gut ist.
überlege dir ev die Textfelder nicht editierbar zu machen und einen JSpinner für die Reaktionszeit zu verwenden.

SGT.Hawk
2005-01-02, 16:11:23
Ich muss allerdings zugeben,dass ich mich erst seit 1 Jahr mit JAVA beschäftige,vorher nur C gemacht,aber das mal nebenbei.
Hmm,ich bekomme keine Exceptions!Was du meinst ist,wahrscheinlich ein
ConcurrentModificationException,aber bekomme ich nicht,merkwürdig.

Mit der ArrayList,das war nur eine Prüfung meinerseits,um zu sehen,ob er alles wirklich speichert an Aktionen,aber weiss meinst du mit
"du verwendest zwar ArrayList, was gut ist, programmierst aber gegen die Klasse und nicht das Interface List, was weniger gut ist."
An sich finde ich auch das der RandomSolver nicht unbedingt notwendig als Thread ist das ja sowieso,der EVent-Dispatch Thread sich um die Klicks kümmern könnte oder irre ich mich?
Mit den Änderungen der Swing Komponenten meinst du in der run Methode von RandomNumber,wo ich Fonts und die Grösse ändere?Aber,wenn die Zahlen nur mal gleich sind,wie soll ich das anders machen?
Andereseits habe ich gelesen,dass man im Event Dispatch nicht so viel Code
ausführen sollte,da das alle in einem Thread gemacht wird und die GUI einfrieren könnte.Bitte klär mich genauer auf!

PS:Mit dem Uimanger habe ich geändert,war vielleicht blöd gedacht.
PS:Oh,doch man kriegt eine Exception mit der ArrayList,das kann ich lösen,muss man natürlich auch syncronized machen!Die andere Exception kapiere ich nicht!

HellHorse
2005-01-02, 17:07:54
aber weiss meinst du mit "du verwendest zwar ArrayList, was gut ist, programmierst aber gegen die Klasse und nicht das Interface List, was weniger gut ist."
Soll heissen der statische Typ der Variable ist eine konkrete Klasse (ArrayList) statt ein Interface (List) was besser wäre.

An sich finde ich auch das der RandomSolver nicht unbedingt notwendig als Thread ist das ja sowieso,der EVent-Dispatch Thread sich um die Klicks kümmern könnte oder irre ich mich?
Nein, tust du nicht. Du rufst darauf ja auch nie #start auf, was einen neuen Thread öffnen würde.


Mit den Änderungen der Swing Komponenten meinst du in der run Methode von RandomNumber,wo ich Fonts und die Grösse ändere?
gaynau, aber auch das Lesen

Andereseits habe ich gelesen,dass man im Event Dispatch nicht so viel Code
ausführen sollte,da das alle in einem Thread gemacht wird und die GUI einfrieren könnte.
Codegrösse ist nie das Problem, sondern die Ausführungszeit, die der Code benötigt. Zwei Zahlen zu vergleichen, zwei bis vier Fonts zu kerieren und setzen, einen boolean zu setzen, eine Stringkonkatenation und drei Strings zu setzen läuft sicher nicht unter viel Arbeit. Ein File aus den Internet herunterzuladen kann jedoch einen Moment dauern auch wenn es nur eine Zeile Code ist und sollte daher nicht im Event Dispatch gemacht werden.

Bitte klär mich genauer auf!
Wie schon erwähnt wurde ist Swing (mit Ausnahmen) nicht thread-sicher, das heisst du muss selbst dafür sorgen. Der einfachste Weg ist die Komponenten nur im Event Dispatch Thread zu modifizieren und zuzugreiffen. Die drei geposteten Links sollten eigentlich alles erklären. Schlussendlich wirst du bei SwingUtilities.invokeAndWait und SwingUtilities.invokeLater landen.

PS: Oh,doch man kriegt eine Exception mit der ArrayList,das kann ich lösen,muss man natürlich auch syncronized machen!Die andere Exception kapiere ich nicht!
Das ist das fiese an Threading-Problemen, sie sind nicht 100% reproduzierbar.
synchronized alleine wird das Problem vermutlich nicht lösen. Aber eine Kombination von Collections.synchronzedList und dem (http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collections.html#synchronizedList(java.util.List))

SGT.Hawk
2005-01-02, 17:31:33
löschen

SGT.Hawk
2005-01-02, 17:34:13
Ok,danke für alles.Bis zum nächsten Mal.
Ja,leider benutze ich noch nicht Java 1.5,da das Eclipse noch nicht unterstützt,aber das finde ich gerade an Threads spannend,wenn man es selber lösen kann mit Gehirnschmalz,als vorgefertigte Klassen,jetzt jedenfalls,später,wenn man es begriffen hat,würde ich die Klassen nutzen,aber erstmal will ich mich damit auseinandersetzen.Ach ja man sollte die Events mit einer anonymen Klasse machen,aber da müssen ja immer die Variablen final sein und manchmal greift man auf welche zu die nicht final sind und dann geht es nicht und aus Bequemlickeit habe ich einfach lokale Klassen genommen.

HellHorse
2005-01-02, 20:41:50
Ach ja man sollte die Events mit einer anonymen Klasse machen,aber da müssen ja immer die Variablen final sein und manchmal greift man auf welche zu die nicht final sind und dann geht es nicht und aus Bequemlickeit habe ich einfach lokale Klassen genommen.
Stimmt schon, schlussendlich läuft es auf's Gleiche raus. Aber lokale Klassen sind weniger geläufig und bringen nichts ggü annonymen Klassen wenn man sie bloss einmal instantiiert. Dadruch, dass sie so selten verwendet werden, können sie verwirren. Für alles, was mehr als eine oder zwei Zeilen Code ist lieber innere oder "richige" Klassen nehmen.

Wenn du die Nebenläufigkeitshelferchen von Java 1.5 in Java 1.4 willst, kannst du sie auch direkt von Dough Lea nehmen (ist sogar noch etwas mehr).
http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html
oder den backport:
http://www.mathcs.emory.edu/dcl/util/backport-util-concurrent/

edit:
Falls du Collections.synchronizedList meinst, das gibt's schon seit 1.2 oder so. Mit 1.5 wurde es lediglich generisch.

SGT.Hawk
2005-01-04, 00:07:02
Achja,wie sieht es bei SWT aus,der ja Heavyweight-Components benutzt im Gegensatz zu SWING.Darf auch da nur im Event Thread die Components ändern oder ist das auch nicht thrwad-safe?
Mfg Hawk

HellHorse
2005-01-04, 01:39:29
Bei SWT sieht es (surprise, surprise) genau gleich aus! Die Methoden heissen asyncExec (invokeLaker) und syncExec (invokeAndWait) und sind Display statt einer Hilfsklasse zugeordnet. Zumindest bei Azureus gibt's eine schöne Exception, wenn du dich nicht dran hälst. Du hast also was gelernt was du auch sonst anwenden kannst. :wink:

Und SWT macht sowohl light- wie heavyweight da sie rausgefunden haben, dass heavyweight alleine nicht glücklich macht (die fortgeschrittenen Widgets haben komischerweise keine native Entsprechung und viele Einschräkungen kommen davon, dass man auf heayweight setzt [ich will das Icon im Button rechts von Text, auf allen Plattformen]). Und wenn du dir "Eclipse Forms Programming Guide" anschaust wird dort verklausiliert ausgedrückt, dass bloss lightweight alleine eigentlich cool wäre.

Die meisten GUI-Toolkits sind afaik nicht thread-safe.