# Zugriff zweier Threads auf diesselbe Methode



## miles (24. Apr 2012)

Guten Tag,

ich schreibe derzeit an einem Programm, welches Nachrichten über die Netzwerkschnittstelle an einen NetIO sendet. Der NetIO antwortet bzw. sendet je nach Anfrage einen String mit unterschiedlichem Inhalt zurück.

Ein Thread sendet Permanent Anfragen um Analogwerte abzufragen. Ein anderer Thread sendet "gleichzeitig" den Befehl zum Ein- und Ausschalten eines Digital-Ports. Hierbei kommen sich beide Threads in die Quere, denn sie greifen beide auf diesselbe Methode, zum Senden der Daten, zu! Sie erwarten aber jeweils einen anderen Wert ("setPort()" erwartet ein "ACK" und "getADC()" einen Wert zwischen 0 und 1023). Das geht leider regelmäßig schief.

Wie schaffe ich es, die einzelnen Threads (die bei mir einzelne Klassen sind) zu synchronisieren? Ich habe schon ein wenig über "sychronized" gelesen. Kann ich beispielsweise ein Objekt erzeugen und den beiden Threads im synchronized-Aufruf mitgeben? Regelt Java dann von selbst, wann welcher Thread auf die Methode(n) in der main-Klasse zugreifen darf und wartet ab bis diese ihre Antwort vom NetIO erhalten hat. Oder gibt es eine andere Möglichkeit?

Vielen Dank im voraus
Dominic


Die Methode zum Senden von Daten:

```
private String sendData(String msg)
{
        String read = "";
         try
	 {
                PrintWriter pw = new PrintWriter(socket.getOutputStream());
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                pw.println(msg+"\r\n");
                pw.flush();
                read = in.readLine();
                read = read.replaceAll("\0", "").replaceAll("\r\n", "").trim();
            }
	    catch (IOException e)
	    {
                e.printStackTrace();
            }
        }
        return read;
}
```

Die Methode zum Setzen des Ports:

```
public boolean setPort(int port, boolean level)
    {
        if(port < 1 || port > 8)
        {
            return false;
        }

        char levelchar = level ? '1' : '0';
        return sendData("SETPORT "+port+"."+levelchar).equals("ACK");
    }
```

Die Methode zum Abfragen des AnalogInputs:

```
public int getADC(int port)
    {
        if(port < 1 || port > 4)
        {
            return -1;
        }
        return Integer.parseInt(sendData("GETADC "+port));
    }
```


----------



## nillehammer (24. Apr 2012)

Das einfachste ist, das Schlüsselwort "synchronized" vor die Methoden zu setzen, von denen Du möchtest, dass sie garantiert nacheinander verwendet werden.


----------



## miles (24. Apr 2012)

Erstmal Dankeschön!
Das heißt, vor den Methodenaufruf im Thread oder im Main-Thread in der Methode selbst?

Wie z.B.:

```
public synchronized int getADC(int port)
{
        if(port < 1 || port > 4)
        {
            return -1;
        }
        return Integer.parseInt(sendData("GETADC "+port));
}
```

Oder vor die sendData()-Methode, da ich ja aus meinen Threads im Prinzip schon zwei verschiedene (von einander unabhängige) Methoden (getADC und setPort) aufrufe, die wiederrum erst sendData aufrufen?


----------



## GUI-Programmer (24. Apr 2012)

Aber noch was: Wenn die Methoden keine Klassenvariablen bzw. statische Variablen ändern, sondern wirklich nur interne Variablen (innerhalb der einzelnen Methoden), dann brauchts du in der Regel da eigentlich gar nichts zu synchronisieren.


----------



## miles (24. Apr 2012)

Nein, Instanzvariablen werden nicht geändert, aber mein Problem ist folgendes:

Die Methode getADC wird alle 50ms von einem externen Thread aufgerufen. Diese ruft die Methode sendData auf. SendData liest einen String ein und gibt ihn der Methode zurück. GetADC gibt den Rückgabewert von sendData zurück. Dieser Wert wird in einen Vector geschrieben. Jetzt kommt irgendwann die Methode setPort zum Einsatz, diese greift ebenfalls auf sendData zu.

Ich bekomme eine Fehlermeldung die besagt:
Exception in thread "Thread-3" java.lang.NumberFormatException For Input String: "ACK"
Thread-3 ist der Thread, der setPort aufruft.

Ich vermute, dass die Methode setPort noch einen "Zahlen-String" zurückbekommt. Außerdem habe ich das Problem erst, wenn ich beide Threads starte und der Befehl setPort() kommt. Manchmal klappt es 13mal, dann nicht mehr. Und ich erinnere mich an mein Java-Buch, dass dort solch ein Problem geschildert war, wenn Threads nicht synchronisiert sind.

Vielen Dank


----------



## Volvagia (24. Apr 2012)

Du teilst ja die Antworten nicht richtig zu. Wenn A "ABC" und B "DEF" sendet kann es sein, dass B die Antwort von A und umgekehrt bekommt, wenn wie wild darauf losgelesen wird. Das wird auch dein Fehler sein, da er laut Fehlermeldung den String "ACK" nicht in eine Zahl umwandeln kann.
Oder die Threads empfangen auch vollkommen verschieden, weil z. B. abwechselnd ein Zeichen gelesen wird.

Ich würde dir empfehlen, dass nur 1 Thread ständig am Reader hängt und ließt. Alles gelesene kommt in eine Collection (ich verwende in meinen Programm eine LinkedBlockingQueue). Dazu wird für jede Anfrage eine ID generiert. Der Server ließt die ID aus und sendet sie bei der Antwort zurück. So kann jederzeit zwischen jeder Anfrage und ihrer Antwort unterschieden werden. Das heißt, falls 2x die selbe Anfrage von 2 Parts des Programmes gestellt wird, da die Anfrage "ABC" und "DEF" sowieso verschiedene Antwortstrings zurückerhalten würden, z. B. ABC_ACK;12345 oder DEF_ACK oder auch mal DEF_NAK, oder so.


----------



## miles (24. Apr 2012)

Okay, das klingt plausibel. LinkedBlockingQueue - Dann werde ich mich mal damit befassen, denn das klingt stark nach einer Lösung. 

Ich dachte allerdings, das übernimmt die Netzwerkschnittstelle, da diese das TCP/IP-Protokoll verwendet. Da dies Paketorientiert arbeitet, nahm ich an, werden Antworten den Fragen mithilfe von Sequenz- und Acknowledgementnummer zugeordnet. Auf der Leitung, bzw. auf der Netzwerkschnittstelle passiert dies auch so (habe ich mit WireShark kontrolliert). Das Problem liegt dann wohl zwischen der Netzwerkschnittstelle und Java, oder? Also kann ich das Synchronisationsproblem ausschließen?

Nur ein Thread pro Reader - Es ist dann wohl nicht damit getan, dass ich die Methode selbst in jeden Thread einzeln schreibe, da die Daten ja trotzdem nicht in der richtigen Reihenfolge im Java-Programm bzw. im Reader ankommen, oder?


----------



## Volvagia (24. Apr 2012)

Sie kommen in der richtigen Reihenfolge an, dass ist ja einer der Vorteile gegenüber UDP. Nur wer sich im Programm die Daten holt liegt außerhalb der Macht von TCP. Die LBQ blockiert einfach den oder die anfragenden Thread(s) solange sie leer ist. Danach sollte sich jeder Thread der Daten will anschauen, ob der Name oder wie man das auch immer nennt (z. B. SET_PORT_ACK) dem entspricht, dass er haben will. Wenn nicht schiebt er es zurück in die Collection und pausiert einen kurzen Moment um Endlosschleifen zu vermeiden, falls der Thread, für dem die Daten eigendlich bestimmt sind beschäftigt ist. (Sonst würde der lesende Thread "ohne" Unterbrechung ständig die selben Daten aus der Collection lesen und wieder zurückschieben). Wenn der Name der Daten passt liefert es den String zurück, extrahiert eventuelle Parameter (wie dem Port) usw.


Sag das mit WireShark besser nicht zu laut, die Regierung hat vor jedes Wissen aus den Menschen zu vertreiben und deshalb den Besitzt zusammen mit anderen Programmen verboten.


----------



## miles (24. Apr 2012)

Okay, dann verstehe ich nur eine Sache noch nicht ganz:

In einem Thread frage ich nacheinander drei Analogeingänge ab und trage die Werte in ein Diagramm ein. Anhand dieser Daten kann ich mit Sicherheit sagen, dass die Werte dem Eingang entsprechen, den ich abfrage.
Zwischen diesen Abfragen ist keine thread.sleep() Funktion eingebaut oder ähnliches, sodass es ja dann eigentlich passieren müsste, dass ab und an mal der falsche Wert zugeordnet wird. Aber in den Diagrammen sind keine Ausreisser o.ä. zu erkennen. Und dort weise ich ja auch nicht explizit Antworten zu, oder?
Außerdem habe ich gelernt, dass Methoden nacheinander komplett abgearbeitet werden. Dann liegt es also nur am "zweiten" Thread, der ungehindert auf die Antworten (die irgendwo in einer Queue o.ä. liegen)  zugreift und nicht weiß, dass sie nicht für ihn bestimmt sind!?

Ich möchte das Problem gerne (komplett) verstehen, deshalb die vielen Fragen.


----------



## Volvagia (25. Apr 2012)

Wenns nur 1 Thread ist ist es ja kein Problem. Er nimmt die Daten und trägt sie ins Diagramm rein. Ich nehme an mit "Analogeingang" meinst du einen Socket? Problematisch ist es ja nur wenn mehrere Threads vom selben Socket lesen und sich das schnappen was sie in die Finger bekommen.

Antworten vom Server kommen wahrscheinlich auch in der richtigen Reihenfolge zurück. Ich nehme an du hast pro Socket einen Thread der Anfragen annimmt, durchführt und eine Antwort sendet. Aber du weißt nicht ob der Thread, der zuerst sendet auch zuerst liest, selbst wenn es in jeden Fall sofort in der nächsten Codezeile steht. Nach dem Senden kann der Thread pausiert werden und der andere kann in einen Durchgang senden und die Antwort des anderen Threads lesen. Bei heutigen Betriebsystemen hast du (zum Glück) keinen Einfluss darauf, wieviel Zeit jeweils zu Verfügung stehen, du musst nur irgendwie dafür sorgen, das der Thread auch die Daten bekommt, die für ihm bestimmt sind.

Bei mir sieht die Authentication z. B. so aus:

```
57$14$AuthenticateRQ3$kai32$493ffb457690726667b7a4379b74204e
```

Als allererstes die Länge des kompletten Befehls ab dem ersten Trennzeichen, da ich auch Userdefinierten Text übertragen will und ein Trennzeichen im Text nicht vorkommen darf. Dann ein Trennzeichen um das Ende der Zahl anzugeben. Dahinter die Länge des Kommandos, Trennzeichen, Kommando, Länge des Parameters, Trennzeichen, Parameter, usw.

Die Antwort könnte so aussehen:


```
33$30$AuthenticateNAKWrongAccessData
```

oder eben auch so:


```
18$15$AuthenticateACK
```

Dafür habe ich eine kleine Klasse geschrieben. Der Befehl wird per Parameter übergeben, per Add-Methoden werden Parameter (String, boolean oder Zahlen) als String gespeichert, toString wandelt das ganze in so eine Zeichenkette um und sendet es. Empfangener Text wird auf der gegenüberliegenden Seite an einen anderen Konstruktor übergeben, der den Inhalt aus den String rekonstruiert.

Lesen geht dann auch ganz einfach.

Wartet auf bestimmten Befehl mit bestimmten Parameter an bestimmter Stelle (meistens eine Zahl in Field 0, um die Antwort auf eine Anfrage durch die ID zu bekommen.)

```
public static Command waitForParameter(int field, String parm, Order... order) {
	Command cmd = null;
	do {
		Command rdCommand = Command.read(order);
		if(rdCommand.getParameterSize() >= (field + 1) && rdCommand.getParameter(field).equals(parm)) {
			cmd = rdCommand;
		}
		else {
			rdCommand.pushBack(TimeUnit.MILLISECONDS, 1L);
		}
	}
	while(cmd == null);
	return(cmd);
}
```

Wartet auf einen bestimmten Befehl:


```
public static Command read(Order... order) {
	return(read(TimeUnit.MILLISECONDS, 10L, order));
}
public static Command read(TimeUnit timeUnit, long timeFactor, Order... order) {
	Command cmd = null;
	try {
		cmd = read(timeUnit, timeFactor, false, order);
	}
	catch (InterruptedException e) {}
	return(cmd);
}
public static Command read(TimeUnit timeUnit, long timeFactor, boolean interruptable, Order... order)
throws InterruptedException
{
	Command cmd = null;
	boolean found;
	do {
		boolean interrupted = Thread.interrupted();
		if(interruptable && interrupted) {
			throw new InterruptedException();
		}
		
		cmd = read(interruptable);
		if(cmd == null) {
			throw new InterruptedException();
		}
		String commandText = cmd.getCommand().toString();
		found = false;

		for(Order o:order) {
			if(commandText.equals(o.toString())) {
				found = true;
				break;
			}
		}
		
		if(!found) {
			cmd.pushBack(timeUnit, timeFactor);
		}
	}
	while(!found);
	return(cmd);
}
public static Command read(boolean interruptable) {		
	Command cmd = null;
	
	do {
		try {
			cmd = nextCommandsQueue.take();
		}
		catch (NoSuchElementException e) {}
		catch (InterruptedException e)
		{
			if(interruptable) {
				return(null);
			}
			Thread.interrupted();
		}

		if(cmd == null) {
			try {
				TimeUnit.MILLISECONDS.sleep(10L);
			} catch (InterruptedException e) {
				if(interruptable) {
					return(null);
				}
				Thread.interrupted();
			}
		}
	}
	while(cmd == null);
	return(cmd);
}
```


----------



## miles (25. Apr 2012)

> Ich nehme an mit "Analogeingang" meinst du einen Socket?


Nein, nur um das noch schnell klarzustellen: Ich besitze einen sogenannten WebIO, ein Gerät, welches über digitale Ein- und Ausgänge zum Schalten von Verbrauchern verfügt. Ebenso vorhanden sind Analoge Eingänge um Spannungen zu erfassen. Diese Werte werden von einem Ethernet-Controller zur Verfügung gestellt und können mit oben genanntem Befehl (getADC) über das Netzwerk abgefragt werden. Je nach Spannung die am analogen Eingang anliegt, beträgt der Wert zwischen 0 und 1023.



> Ich nehme an du hast pro Socket einen Thread der Anfragen annimmt, durchführt und eine Antwort sendet.


Nein, ich habe einen Main-Thread in dem ein Socket erzeugt wird. Auf diesen Socket greift die Methode sendData zu.

So, den Java-Code schaue ich mir im laufe des Tages mal genauer an. Vielen Dank schonmal für die Hilfe und gute Nacht.


----------



## miles (25. Apr 2012)

Okay, ich habe mir mal den Java-Code angeschaut. So wie ich es sehe, muss die gegenüberliegende Seite eine ID wie:

```
18$15$AuthenticateACK
```
mitsenden. Da ich aber keinen Einfluss darauf habe, was mir der WebIO antwortet, fällt diese Möglichkeit vermutlich raus.

Es muss doch möglich sein, die zwei bzw. drei Threads so zu Synchonisieren, dass ein Thread eine Methode aufruft, komplett durchläuft und erst anschließend der andere Thread die Methode aufrufen darf, diese dann komplett durchläuft usw., oder etwa nicht?

Thread1:
getADC(2)
->sendData("GETADC 2")
-> Antwort erhalten
return;

Thread 2:
setPort(1, true)
->sendData("SETPORT 1.1")
->Antwort erhalten
return;

Thread1:
getADC(2)
->sendData("GETADC 2")
-> Antwort erhalten
return;

usw...


----------



## Volvagia (25. Apr 2012)

Ja. Das ginge so:


```
public void tueWas(Socket socket) {
	synchronized(socket) {
		write("Text");
		return(read(socket));
	}
}
public void tueWasAnderes(Socket socket) {
	synchronized(socket) {
		write("Anderer Text");
		return(Integer.parseInt(read(socket)));
	}
}
```

So wird der Inhalt der Methode erst abgearbeitet, wenn der Socket noch nicht in Benutzung ist.


----------



## miles (25. Apr 2012)

Hallo,

ich habe das Problem gelöst! :applaus:

Es reicht, wenn man die Methode sendData() synchronisiert, denn so steht die komplette Methode im Synchronisierungsrumpf.

Ich erkläre mir das ganze so: Einer der drei Threads schnappt sich die Referenz und ist berechtigt sendData auszuführen. Vorher rufen alle auf, was sie eh aufgerufen hätten. Der eine Thread getADC, der andere setPort. An der Stelle, an der sendData aufgerufen wird, hat der Thread vorrang, der sich die Referenz geschnappt hat.
So führt jeder Thread, der die Methode sendData() berechtigterweise aufruft, einen Flash durch, damit seine lokale Variablen den Variablen im globalen Speicher entsprechen. Anschließend wird der Sync-Rumpf, also die sendData-Methode, ausgeführt. Nun flusht der Thread noch den Lokalen Speicher und schreibt die lokalen Variablen in den globalen Speicher.
Als letztes gibt er den Lock wieder frei und der nächste Thread darf sendData benutzen.
Ich dachte zuerst, es muss getADC synchonisiert werden, aber es reicht auch nur sendData!

Sind meine Überlegungen richtig?

Grüße und vielen Dank Volvagia

Dominic


----------



## FArt (26. Apr 2012)

Wenn die NumberFormatException auftritt, weil das Format nicht passt, dann ist das ein systematischer Fehler und keine Syncrhonisationsproblem.
Wenn die Formatierung nicht funktioniert, weil konkurrierend formatiert wird, dann ist das klar, weil das so auch in der Doku steht: 


> Formatters are not necessarily safe for multithreaded access. Thread safety is optional and is the responsibility of users of methods in this class.


[JAPI]http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html[/JAPI]
Dann reicht es aber auch lediglich die Formatierung zu synchronisieren.


----------



## Volvagia (26. Apr 2012)

Das Problem war ja nicht der Formatter an sich.
setPort und getADC haben beide eine Methode benutzt die einen String zum Server schickt und sofort die Antwort ließt. Da das unsynchron war hat getADC (dass eine Integer vom Server erwartet) die Antwort von setPort ("ACK") erhalten.

Wie ein sychronized-Block tatsächlich funktioniert weiß ich nicht. Nur, dass bloß 1 Thread gleichzeitig in einen Block der auf ein Objekt synchronisiert ist sein kann. Wenn es keine Probleme gibt beschäftige ich mich kaum damit was dahinter liegt. Meistens ist es sowieso nicht wirklich durchsichtig da auf Interface o. ä. reduziert wird, und wenn ich für jeden Vorgang erstmal den Code der API durchlesen müsste (jetzt im Beispiel von Source, synchronized liegt ja noch viel tiefer) würde ich zu garnichts mehr kommen.


----------



## miles (26. Apr 2012)

Ja, das kann ich mir vorstellen! Ich möchte das Problem ja auch nicht bis ins OS und dessen Tiefen verfolgen, sondern einfach verstehen was passiert ist. Bisher habe ich beim Lösen von Problemen (gefühlt) am meisten gelernt. Und da ich noch in den Kinderschuhen stecke, was Java betrifft und keinen kompetenten Ansprechpartner im privaten Umfeld habe, ist das oft die einzige Lösung!
Ansonsten konnte ich bisher auf fast alle meine Fragen Antworten im Forum finden!

Grüße


----------

