# Was tun gegen Timeouts auf der Serverseite?



## gutenacht-steph (28. Sep 2010)

Hallo,

ich bin noch an meinem Client/Server Programm.

Der Server wartet traditionell auf eingehende Verbindungen und startet bei jeder akzeptierten Verbindung einen extrigen Thread.

Bei meinem Serverteil möchte ich nun etwas gegen Timeouts unternehmen können. Falls beim abarbeiten des gestarteten Threads (dort werden Daten von der DB abgefrat) irgendwelche Deadlocks o. ä. zustande kommen würden.

Dafür startet jeder neue Thread einen parallelen (Thread Watcher) Thread, der für 10 Sekunden schläft, und seinen "Vater", der ihn erschaffen hat, zerstört (falls dieser noch existieren sollte).


Hmm, bin mir jetzt allerdings nicht sicher ob das wirklich reicht. Was meint ihr?

Würde mich über neue Vorschläge und Anregungen sehr freuen.


----------



## Gast2 (28. Sep 2010)

gutenacht-steph hat gesagt.:


> Bei meinem Serverteil möchte ich nun etwas gegen Timeouts unternehmen können. Falls beim abarbeiten des gestarteten Threads (dort werden Daten von der DB abgefrat) irgendwelche Deadlocks o. ä. zustande kommen würden.
> 
> Dafür startet jeder neue Thread einen parallelen (Thread Watcher) Thread, der für 10 Sekunden schläft, und seinen "Vater", der ihn erschaffen hat, zerstört (falls dieser noch existieren sollte).



Ganz ehrlich klingt das nicht so gut... Je mehr Threads umso höher steigt die Kompexität deiner Anwendung. Threads aus anderen Thred zu "killen" ist auch nicht so sehr schön finde ich. Besser ist es Zombie-Threads gar nicht erst entstehen zu lassen. Und dein Problem scheint mir auf den ersten Blick erstmal trivial.

Angenommen:
Thread A startet und setzt ein lock auf eine Tabelle und fängt an komplizierte langwierige Rechnungen zu machen. Thread B startet und will ebenso den lock auf die Tabelle setzten. Dann bekommt Thread B eine SQLException und weiß das momentan die Tabelle gesperrt ist. Also wartet Thread B eine zeitlang und probiert z.B. alle 5 Sekundne wieder ob er den lock bekommen kann. Wenn nach 30 Sekunden Thread B nie den lock bekommt bricht seine warte schleife ab und er verlässt seine run-Methode.

Wofür willst du da jetzt einen Thread der die Threads überwacht?

Zumal man relativ gut etwas gegen Deadlocks machen kann, grade bei einer Datenbank Anwendung solltest du dir nochmal genau überlegen ob deine Architektur so sinnvoll ist.


----------



## SlaterB (28. Sep 2010)

ein Thread X kann von außen nicht beendet werden, jedenfalls nicht immer,
falls X von Ressourcen wie Sockets/ Streams/ anderen Threads abhängt, die man so modifizieren kann, dass sie etwa blockierende Aufrufe beenden, so wird auf X Einfluss genommen,
wenn aber ne fehlerhafte normale interne while(true)-Schleife läuft, ist man aufgeschmissen

davon abgesehen gibt es natürlich die normalen Bearbeitungen eines Threads, die dieser aktiv zuläßt,
etwa eine boolean-Variable setzen, die regelmäßig von X geprüft wird, damit X sich dann SELBER beendet


----------



## gutenacht-steph (29. Sep 2010)

Ok, aber ich soll die Threads von einem anderen "überwachen" lassen und nach einer bestimmten Zeit "abwürgen", falls dieser z.B. bei einer I/O Operation stecken geblieben sein sollte.

Wenn ich den Vater Thread dann im Kind Thread sozusagen nach den 10 Sekunden auf null setze wird dieser dann gekillt? Als ich es ausprobierte gab mir das kein aufschlussreiches Ergebnis.

Wo ist allerdings das Problem wenn ich im Vaterthread, der eigtl zerstört werden soll bei fehlerhaften Verhalten, bei korrekten Verhalten das Kind töte (dieses Kind hat jedenfalls keine Abhängigkeit zur DB oder ähnlichen Sachen, dort wird lediglich der vater.stop() aufgerufen)

Danke für die Antworten jedenfalls


----------



## Gast2 (29. Sep 2010)

Du solltest stop() nicht brauchen. 


```
ThreadA implements Runnable {

  private volaitale boolean isRunning = false;
  private long timeOut = 10000;
  private long timeSpent = 0;

  public void run(){
     isRunning = true;
     while(isRunning){
      
     // mach was
     if(timeSpent >= timeOut){
        isRunning = false;
     }
  }

  public void stopThread(){
     isRunning = false;
  }

  public boolean isThreadRunning(){
    return isRunning;
  }
}
```

So kannst du ganz beqeuem den Status abfragen oder auch den Thread beenden ohne auf drastische Mittel zu verzichten. Wenn der Thread selber bei einer IOOperation hängen bleibt und die underliegende Operatin bei einem Timeout keine IOException wirft ist das allerdings eher schlecht. Aber das würde ich erstmal prüfen. Oftmals bekommst du schon einen Timout aus der API wenn eine IO Operation hängt - z.B. einen SocketException wenn auf dem Netzwerk ein Timeout auftritt.


----------



## gutenacht-steph (29. Sep 2010)

Vielen Dank für dein Code Beispiel.

Aber wenn der Thread A im while(isRunning) Teil stecken bleiben sollte, was nützt es dann die stopThread() Methode von außen aufzurufen und den Zustand auf false zu setzen? Die while Schleife würde doch dann gar nicht mehr dazu kommen, die running-Bedingung zu prüfen, oder?


----------



## SlaterB (29. Sep 2010)

kommt auf die Art des Steckenbleibens an, 
die Variable hilft natürlich nur wenn jeder Schleifendurchlauf begrenzt ist und so die Schleifenbedingung spätestens alle x Sekunden geprüft wird

eine Käfigtür zu öffnen hilft einem Tier nur genau dann, wenn es dies auch realisiert/ regelmäßig an der Tür vorbeikommt und rausgehen kann/will,
wenn es sich im Käfig-Labyrinth dahinter verlaufen hat oder schon zu schwach ist sich zu bewegen oder von der Tür nix weiß, dann bringt das wenig, richtig..


----------



## gutenacht-steph (29. Sep 2010)

Danke für die animalische Metapher SlaterB . 

Ich werde mir noch ein paar Gedanken darüber machen.


----------



## Gast2 (29. Sep 2010)

gutenacht-steph hat gesagt.:


> Die while Schleife würde doch dann gar nicht mehr dazu kommen, die running-Bedingung zu prüfen, oder?



Deswegen sagte ich ja auch:



			
				fassy hat gesagt.:
			
		

> Wenn der Thread selber bei einer IOOperation hängen bleibt und die underliegende Operatin bei einem Timeout keine IOException wirft ist das allerdings eher schlecht. Aber das würde ich erstmal prüfen. Oftmals bekommst du schon einen Timout aus der API wenn eine IO Operation hängt - z.B. einen SocketException wenn auf dem Netzwerk ein Timeout auftritt.


----------



## kay73 (29. Sep 2010)

Hi,

Sun schlägt zwar vor, innerhalb eines Threads irgendeine Bedingung zu prüfen, anhand derer ein Thread aktiv beendet werden kann, doch ist das nicht immer möglich, sodaß man auf 
	
	
	
	





```
Thread.interrupt()
```
 zurückgreifen muss. Und nicht auf stop(), das deprecated ist.

Dabei ist zu beachten, dass sich ein Thread darum i. A. nicht die Bohne schert: 

Wenn eine "Rechenschleife" läuft, wird nur das interrupted-flag auf true gesetzt, der Thread läuft aber weiter und man muss manuell auf interrupted testen.

Wenn ein Thread innerhalb von 
	
	
	
	





```
Object.wait()
```
 oder 
	
	
	
	





```
Thread.sleep()
```
 blockiert, fliegt eine 
	
	
	
	





```
InterruptedException
```
 in deren catch man auf eine Abbruchbedingung testen kann.

Wenn ein Thread auf einem alten IO-call blockiert, passiert überhaupt nichts, daher enweder die Socket.timeout Eigenschaft ab Java 5 nutzen oder besser auf SocketChannels (die ein InterruptableChannel implementieren) ausweichen.

Ausserdem ist das Neuerzeugen von zig "Watchdog"-Threads Overkill; das geht auch mit einem einzigen. Und der ExecutorService im concurrent Paket kann Threads besser recyceln. Die Watchdog-Klasse merkt sich, wann ein Runnable oder Callable gestartet wurde und ein einziger thread iteriert über alle laufenden "Prozesse" und schießt die, die schon zu lange laufen ab.

Dazu wird ein Server gestartet, der maximal 30 Sekunden läuft und jeder Client wird nach maximal 20 Sekunden beendet. Einzig doof ist, dass bei den SocketChannels irgendwie die Ausgabe nicht rausgeschrieben wird. Vielleicht fixt das ja jemand... ;-)


```
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Logger;

class Watchdog {
	static class TimedFutureHolder {
	
		private final long start = System.currentTimeMillis();
		
		private final long timeout;
		
		private final Future<?> future;

		public TimedFutureHolder(final Future<?> future, long timeout) {
			this.timeout = timeout;
			this.future = future;
		}
		
		public Future<?> getFuture() { 	return future; }
		public long getStart() { return start; }
		public long getTimeout() { return timeout; }
	}
	
	private static Watchdog singleton = null;
	
	private Watchdog() {
		watchdogThread.start();
	}
	
	public static Watchdog getInstance() {
		synchronized(Watchdog.class) {
			if(singleton == null) {
				singleton = new Watchdog();
			}	
		}
		
		return singleton;
	}
	
	private final ExecutorService exs = Executors.newCachedThreadPool();

	private final List<TimedFutureHolder> jobList = new ArrayList<TimedFutureHolder>();
	
	private Thread watchdogThread = new Thread( new Runnable() {
		
		@Override
		public void run() {
			while(! Thread.interrupted()) {
				synchronized(jobList) {
					final Iterator<TimedFutureHolder> it = jobList.iterator();
					
					TimedFutureHolder timedout = null;
					while(it.hasNext()) {
						final TimedFutureHolder t = it.next();
						if(System.currentTimeMillis() - t.getStart() > t.getTimeout()) {
							timedout = t;
							it.remove();
							break;
						}
					}
					
					if(timedout != null) {					
						timedout.getFuture().cancel(true);
					}
					
					try {
						Thread.sleep(250L);
					} catch (final InterruptedException e) {
						if(exs.isShutdown()) {
							return;
						}
					}
				}
			}
		}
	}); 
	
	public void shutdown() {
		exs.shutdownNow();
		watchdogThread.interrupt();
		watchdogThread = null;
	}
	
	public void executeAndWatch(final Runnable r, final long timeout) {
		synchronized (jobList) {
			jobList.add(new TimedFutureHolder(exs.submit(r), timeout));
		}
	}
	
	public <T> Future<T> executeAndWatch(final Callable<T> c, final long timeout) {
		final Future<T> f = exs.submit(c);
		synchronized (jobList) {
			jobList.add(new TimedFutureHolder(f, timeout));
		}
		
		return f; 
	}
}

public class App {

	public static void main(String[] args) throws InterruptedException {
		
		Watchdog.getInstance().executeAndWatch(new Runnable() {
			public void run() {
				
				final ServerSocketChannel ch;
				final Logger logger = Logger.getLogger("server"); 
				try {
					ch = ServerSocketChannel.open();
					ch.socket().bind(new InetSocketAddress("localhost", 8089));
					logger.info("bound: " +ch);
				}
				catch (IOException e1) {
					logger.severe(e1.getMessage());
					return;
				}
				
				while(true) {
					try {
						final SocketChannel sc = ch.accept();
						logger.info("accept:"+sc);
						
						Watchdog.getInstance().executeAndWatch(new Runnable() {
							
							@Override
							public void run() {
								final long start = System.currentTimeMillis();
								try {
									while(true) {										
										sc.write(ByteBuffer.wrap((("Hallo: "+System.currentTimeMillis()).getBytes())));
										Thread.sleep(500L);
									}
								} catch (final Throwable t) {
									Logger.getAnonymousLogger().severe("thread-"+sc+": down after "+(System.currentTimeMillis() - start)+", "+t.getClass().getSimpleName()+","+t.getMessage());				
								}
							}
						}
						, 3000L);											
					} catch (IOException e) {
						logger.severe("server: "+e.getClass().getSimpleName()+","+e.getMessage());
						break;
					}
				}
			}
		}, 20000L);

		Thread.sleep(30000L);
		
		System.out.println("Watchdog.getInstance().shutdown();");
		Watchdog.getInstance().shutdown();
		
	}
}
```


----------



## SlaterB (29. Sep 2010)

dass eine direkte sleep()-Phase mit interrupt() unterbrochen werden kann bzw. wait() mit notify() oder was es sonst noch
'direkt zur Kommunikation mit Abbruch' an Frameworks gibt, ist ja vollkommen klar,

dagegen eher lustig ist, dass du 'irgendeine Bedingung zu prüfen' bemängelst, dann aber auf das interrupted-boolean-Flag hinweist ist,
das ist doch exakt dasselbe?!


----------



## kay73 (30. Sep 2010)

SlaterB hat gesagt.:


> das ist doch exakt dasselbe?!


Nein. Sun drückt sich allgemeiner aus und meint u. a. Tests auf beliebige Variablen (Programmlogik) z. B. in einem shared scope.


----------

