Socket readObject und wait()

razr

Mitglied
Hi,

wie ihr sicherlich seht ist das mein erster Post in diesem Forum. Ersteinmal möchte ich herzlichst danken. Ihr habt mir bei meinen Google-Suchen schon diverse Male weitergeholfen.
Nun habe ich jedoch ein Problem, dass ich auch hier noch nicht beschrieben gefunden habe:

Ich habe eine Server-Client Software implementiert, die grundsätzlich auch super funktioniert. Ich schicke über den OutputStream (ObjectOutput) eines SSLSockets und empfange in einem eigenen Thread auf dessen InputStream.
Nun habe ich jedoch, in bestimmten Fällen, den Wunsch, ein Objekt des Clients auf dem Stream zu verschicken und die Antwort des Servers direkt wieder zurückzugeben. Dafür will ich den lesenden Thread pausieren (wait()) und anschließend wieder aufwecken, damit dieser nicht das Objekt abfängt.

Mein Code dazu sieht bisher so aus:

...beim Verbinden...
Java:
				listener = new Thread(
					new Runnable() {
						public void run() {
							listen();
						}
					}
				);
				/* Starten des 'lauschenden' Threads */
				listener.start();

Senden auf dem OutputStream
Java:
	public EventmanagerData send(EventmanagerData data) {
		try {
			/* Schlafen legen des 'lauschenden' Threads */
			listener.wait();
			/* Schreiben der Daten in den Stream */
			objOut.writeObject(data);
			objOut.flush();
			EventmanagerData ret = (EventmanagerData) objIn.readObject();
			/* Aufwecken des 'lauschenden' Threads */
			listener.notify();
			/* Rückgabe des Objekts des Servers */
			return ret;
		} /* ... catchen und verarbeiten der Exceptions */

		return null;
	}

Lesen des InputStreams
Java:
	private void listen() {
		while(run) {
			try {
				Object obj = objIn.readObject();
				/* das Object weiter verarbeiten */
			} /* ... catchen und verarbeiten der Exceptions */
		}
	}

Leider erhalte ich immer eine IllegalMonitorStateException:
Code:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalMonitorStateException

Ich habe auch schon jede "Verschachtelung" von synchronisierung versucht. Einziges anderes Ergebnis war, dass der Client sich komplett aufgehangen hat (wahrscheinlich weil irgendwo in der synchronisierung ein after-you after-you auftrat).

Ich fühl mich leider noch nicht so sicher mit dem Synchronisieren von Methoden/Blöcken/etc.

Könnt ihr mir da vielleicht ein paar Hinweise geben, wie ich dieses Problem lösen könnte?

Gruß razr
 
S

SlaterB

Gast
> Ich habe auch schon jede "Verschachtelung" von synchronisierung versucht.

wieso erzählst du dazu nichts genaues und postest einen Code mit wait(), aber ohne jedes synchronized,
der nun nach Lehrbuch garantiert falsch sein muss?

wenn du an einem Objekt x wait() aufrufst, musst du auch synchronized(x) irgendwo haben,
das klingt doch zunächst nicht so verwirrend

-----

> Einziges anderes Ergebnis war, dass der Client sich komplett aufgehangen hat
hast du denn mehrere Objekte zu synchronisieren, auch mehrere wait()?

allgemein als heitere Anmerkung:
wenn ein Auto ohne Räder nicht vorankommt und nach Anbau der Räder das Benzin ausgeht,
dann kann man nicht wirklich den neuen Rädern die Schuld geben,

vielleicht ist dein Programm sowieso im Deadlock, bisher durch die Fehlermeldung noch nicht aufgefallen,
oder es liegt schon daran, muss aber noch richtig gemacht werden,
das neue Problem ist jedenfalls kein Grund, unverzichtbare Arbeitsschritte umgehen zu wollen
 
Zuletzt bearbeitet von einem Moderator:

razr

Mitglied
Was heißt das jetzt genau? Was genau soll den nun synchronisiert werden? Der Gesamte Thread des listeners? Der Abschnitt, der den Thread des listeners anhält?

Wie gesagt: Ich bin mir da immer nicht ganz so sicher...
 
S

SlaterB

Gast
im einfachsten Falle nur um die Zeile
> listener.wait();
drumherum,
wie man einen synchronized-Block definiert dürfte doch wohl bekannt sein, oder ist nachzuschlagen,

> Der Gesamte Thread des listeners?
ist wiederum eher ungenau, synchonisiert wird Code, von Zeile zu Zeile
(mit allen enthaltenen Unter-Aufrufen, wirkt sich aber nicht auf gestartete Threads aus)
oder eine ganze Methode, quasi von Anfangszeile bis Endzeile,
'den Thread' ist keine rechte Zeilenangabe

-----

übrigens wird dadurch nicht 'jemand anders' pausiert oder so,
warten muss immer der Aufrufer selber und zwar bis jemand anders notify() aufruft, sonst sieht es ganz bös aus,
die Konstruktion
Java:
            /* Schlafen legen des 'lauschenden' Threads */
            listener.wait();

            [..]

            /* Aufwecken des 'lauschenden' Threads */
            listener.notify();
wird jetzt, je mehr ich draufschaue, immer falscher

einem anderen Thread kann man an sich nicht reinreden, du kannst einen boolean dort setzen und hoffen,
dass dieser vor bestimmten Aktionen geprüft wird,
falls dieser andere Thread wirklich ein Listener ist, kann man ihn vielleicht auch temporär irgendwo als Listener deregistrieren,
dann bekommt er keine Nachrichten,
wait/notify leistet das aber nicht, eher so zu verwenden:

Java:
listener.gehInDieEcke(); 
// setzt einen boolean, reiner Vorschlag, Synchronisation eher nicht nötig, 
// von eigener Implementierung abhängig

[..] // zwischenzeitlich reagiert listener vielleicht, geht in eine sleep-Schleife,
// oder ruft selber für sich (synchronisiert) wait() auf,
// macht das aber vielleicht sowieso alles, wenn mal nix zu tun ist

listener.duDarfstWieder(); 
listener.notify();
// synchronized nötig, 
// das notify könnte auch in der Methode drinstehen, um nicht vergessen zu werden, 
// bewirkt nichts wenn nicht der Thread (oder sonst wer) 
// per wait() wartet, falls aber doch so wird aufgeweckt
 

razr

Mitglied
Hi,

danke erstmal für die Antworten. Ich habe mir sowas in der Art schon gedacht, und das man immer nur den "eigenen" Thread warten lassen und wieder aufwecken sollte habe ich auch schon an dieversen Stellen gelesen.
Mein Problem war nur, dass das Lesen vom ObjectOutputStream (readObject()) ja blockierend ist, ich dem Thread also gar nicht sagen kann: leg dich jetzt mal schlafen wenn du hier vorbeikommst, weil der nie dort vorbeiläuft.
Ich hatte auch schon versucht über setSoTimeout() auf dem Socket dort eine Unterbrechung alle 100ms zu erreichen und denn Thread dann schlafen zu legen, sollte der andere Thread was machen wollen. Das hatte aber leider den selben Effekt...

Naja ich probiers nochmal so aus. Vielleicht hatte ich da auch noch falsche Synchronisierungen gesetzt.

Ich melde mich, wenn ich ein Ergebnis habe.

Danke

Gruß razr
 

tfa

Top Contributor
@razr
Diese Low-Level-Synchronisiererei mit wait/notify/synchronized ist wirklich ziemlich schwierig zu überblicken.
Schau dir die Möglichkeiten der Concurrency-API an, das ist einfacher. Lock und Condition würde sich hier eventuell anbieten (Beispiel).
 
S

SlaterB

Gast
als Idee noch hineingeworfen:
baue eine Indirektion ein, es gibt eine Komponente, einen Thread X, der verantwortlich für alles Lesen ist,
niemand anders darf readObject() aufrufen, bisher hast du ja mindestens 2 Codestellen,

dem redet dabei niemand rein, die Frage ist aber, was er mit den eingelesenen Objekt macht,
der Standard könnte sein, dass dieses an Listener geht, welches nun vielleicht kein Thread mehr sein muss,
falls es verkraftbar ist, dass die Aktion unter Thread X läuft und solange nicht vom Stream gelesen wird,

dann jedenfalls wäre dein Code in etwa, dass für die nächste Zeit X nicht an Listener weiterleiten soll,
sondern an Y, in der Annahme, dass das nächste Objekt EventmanagerData sein wird,

bisschen komplizierter kann man es noch bauen wenn X andere Objekte, die zufällig in dieser Zeit ankommen,
aufbewahren soll und später wieder an Listener weiterreicht
 

razr

Mitglied
Hey,

ich habe das Ganze jetzt mit locks realisiert. Das funktioniert echt gut! (Danke, tfa, für das Beispiel)

Ich hab nun die methoden jeweils in einen lock laufen lassen und dabei der Methode send eine Priorität gegeben. Damit der listener auch in diesen läuft, hat der socket nun ein SoTimeout bekommen.

Java:
	public EventmanagerData send(EventmanagerData data) {
		try {
			wantsToSend = true;
			listenLock.lock();
			/* Schreiben der Daten in den Stream */
			objOut.writeObject(data);
			objOut.flush();
			/* Rückgabe des Objekts des Servers */
			return (EventmanagerData) objIn.readObject();
		} /* ... catchen und verarbeiten der Exceptions */
		} finally {
			wantsToSend = false;
			listenLock.unlock();
		}
		return null;
	}

Java:
	private void listen() {
		while(run) {
			if(!wantsToSend) {
				try {
					listenLock.lock();
					Object obj = objIn.readObject();
					/* das Object weiter verarbeiten */
				} /* ... catchen und verarbeiten der Exceptions */
				} finally {
					listenLock.unlock();
				}
			}
		}
	}

Das ist jetzt wahrscheinlich keine ganz und gar elegante Lösung, aber mr scheint sie derzeit auszureichen.

Ich hoffe ich habe da nicht irgendwelche groben Fehler drin ;)

Die Thread X Variante hört sich auch interessant an, ich konnte aber jetzt gerade (auf die schnelle) da keine Einarbeitung finden.

Gruß razr
 

Ähnliche Java Themen


Oben