# Multi-Threaded block bei BufferedReader.readLine()



## Beren77 (20. Mai 2009)

Hallo,

ich habe eine Client-Server-Anwendung. Zum Anmelden an den Server sendet der Client einen String und bekommt vom Server eine Antwort. Die Antwort des Servers besteht aus 5 Zeilen, die _immer_ mit einem Carriage-Return (sogar \r\n) abgeschlossen werden.

Starte ich eine Client-Anfrage zum Login, empfange ich auch die entsprechende Antwort. Erst mal kein Problem.
Wenn ich jetzt aber 100 Threads starte, die alle diese Anfrage senden, terminiert das Programm plötzlich nicht mehr immer (sogar nur ziemlich selten). Dann bleibt der Rechner in BufferedReader.readLine() hängen.

Eine google-Suche hat mich zu diesem (alten) Bug-Eintrag für Java 1.5 geführt: Bug ID: 5073414 BufferedReader.readLine() multi-threading issue with line-feed handling

Ich benutze Java 1.6 (U13) und dort war dieser Fix realisiert; trotzdem bin ich noch einen Schritt weitergegangen, habe die Klasse (BufferedReader) kopiert und die gesamte "readLine(boolean)" synchronized gemacht -- ohne Erfolg.

Habt ihr vielleicht eine Idee, was falsch sein könnte?

Ich poste hier mal den gesamten Quellcode meines Testprogramms (Clientseitig) -- sorry, ist etwas lang, aber schon so kurz wie möglich ).

Danke für die Hilfe,
Philipp


```
package com.tensegrity.apidemo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;

import com.tensegrity.palojava.http.HttpParser;

class SimplePaloClient {
	private static final String HOST = "127.0.0.1";
	private static final String PORT = "7778";
	private static final byte[] CRLF = new byte[] {(byte) 13, (byte) 10};

	//some palo server commands:
	private static final String LOGIN_REQUEST = 
		"GET /server/login?user=admin&password=21232f297a57a5a743894a0e4a801fc3 HTTP/1.1\r\n";
	
	private Socket srvSocket;
	private BufferedOutputStream toServer;
	private BufferedInputStream fromServer;
	
	public final synchronized void connect() {
		try {
			srvSocket = new Socket(HOST, Integer.parseInt(PORT));
			toServer = new BufferedOutputStream(srvSocket.getOutputStream());
			fromServer = new BufferedInputStream(srvSocket.getInputStream());
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public final synchronized void disconnect() {
		if(srvSocket == null)
			return;
		try {
			//close all streams...
			toServer.close();
			fromServer.close();
			//and finally the socket:
			srvSocket.close();
			srvSocket = null;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public final synchronized void login() {
		//send login request:
		try {
			send(LOGIN_REQUEST);
		} catch (ConnectException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private final synchronized String send(String request)
			throws ConnectException, IOException {
		System.out.println("REQUEST: "+request);
		try {
			toServer.write(request
					.getBytes(HttpParser.DEFAULT_CHARACTER_ENCODING));
			toServer.write(CRLF);
			toServer.flush();
			// read response:
			String response = readLine();
			return response;
		} catch (SocketException se) {
			se.printStackTrace();
		} catch (InterruptedIOException ie) {
			ie.printStackTrace();
		}
		return null;
	}

	private final synchronized String readLine() throws IOException {
		int cl = -1;
		String line;
		StringBuilder buf = new StringBuilder();
		
		BufferedReader reader = 
			new BufferedReader(new InputStreamReader(fromServer));

		while((line=reader.readLine()) != null) {
			System.out.println("RESPONSE LINE: "+line);
			buf.append(line);
		}
		return buf.toString();
	}
}

public class SimpleClientMultipleThreadTest {
	public static final void main(String[] args) {
		int threads = 100; 
		final int finished[] = {0};
		final int errors[] = {0};
		final ArrayList <SimplePaloClient> clients = 
			new ArrayList<SimplePaloClient>();
		
		for(int i = 0; i < threads; ++i){
			final String name = Integer.toString(i);
			Thread thread = new Thread(new Runnable(){
				public void run() {
					try{						
						SimplePaloClient client = new SimplePaloClient();
						client.connect();
						client.login();
						clients.add(client);
						System.err.println("start thread: "+name);
					}catch(Throwable e){
						e.printStackTrace();
						errors[0]++;
					}
					finished[0]++;
					System.err.println("finished thread: "+name);
					System.err.println("total finished: "+finished[0]);
					
				}
			},Integer.toString(i));
			thread.start();
		}
		while(finished[0] != threads){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		for(SimplePaloClient client : clients)
			client.disconnect();
	}
}
```


----------



## tuxedo (20. Mai 2009)

Da alle 100 Threads eine eigene Socketverbindung mit eigenen Streams besitzen ist das völlig schnuppe. Da musst du die BufferedReader Klasse nicht synchronisieren. 

Vermute dass der Server da nicht sauber funktioniert.

- Alex

[update]

Vllt. sind 100 Anfragen in seeeeehr kurzer Zeit auch zu viel für den Server, so dass der diese blockt oder ins leere laufen lässt weil er einen "Angriff" vermutet?


----------



## Beren77 (20. Mai 2009)

Hmm. "Angeblich" funktioniert der Server. Aber ich werde mir das (zusammen mit den Server-Autoren) noch mal ansehen. Kann man denn ein Fehlverhalten des Clients ausschließen?

Nachtrag: Wenn ich die Anzahl der Threads im Programm auf 1 reduziere und dann das Programm 1000x (schnell hintereinander) starte, funktioniert es einwandfrei...


----------



## tuxedo (20. Mai 2009)

Lass mal die 100 Threads parallel laufen, aber mit 250..500ms Zeitversatz beim starten.

- Alex


----------



## Beren77 (20. Mai 2009)

Jup, hatte ich auch schon versucht: Kein Unterschied...
ABER: Ich habe gerade festgestellt, dass ich vergessen habe, den BufferedReader zu schließen (*ups*). Wenn ich ihn schließe, und somit also auch den InputStream schließe, funktioniert es.
Jaja. Immer die Kleinigkeiten.

Danke für die Hilfe, jedenfalls.


----------



## tuxedo (20. Mai 2009)

Das ist etwas seltsam. Denn die einzelenn Buffer bzw. Streams sind getrennte Instanzen. Könnte höchstens sein dass der Palo-Server keine 100 gleichzeitigen Verbindungen von einer einzigen Adresse zulässt ...

- Alex


----------



## Beren77 (20. Mai 2009)

Ich habe gerade mal nachgefragt, aber dagegen spricht, dass das ursprüngliche Programm ja eben "manchmal" mit 100 Threads funktioniert hat (und sonst verbindet sich gerade keiner mit dem Server, der local läuft). Wenn ich was von den Jungs höre, poste ich es hier...


----------



## tuxedo (20. Mai 2009)

Na dann solltest du auch mal den Code des ursprünglichen Programms mit deinem Vergleichen. Aber am besten werden das die die Entwickler sagen können.

Tippe hier wohl am ehesten auf ein Anti-Flood-Mechanismus. 

- Alex


----------



## Beren77 (20. Mai 2009)

Äh: Mit "ursprünglichem Programm" war mein erstes hier gepostetes gemeint -- ohne das "buffer.close()"...


----------

