# Threads werden nicht beendet



## tme (22. Mrz 2010)

Hallo,

ich schreibe derzeit eine kleine Applikation, die nach gewissen Einstellungen Mails aus einem Repository entnimmt und diese versendet. Um den Mailserver nicht zu hämmern, passiert dies über eine limitierte Anzahl Threads.

Ich erzeuge eine Arrayliste mit dem Primärschlüsseln der Datensätze, welche versendet werden sollen. Diese gebe ich nacheinander jeweils einem Thread mit. Dieser liest dann den entsprechenden Datensatz, bringt die Mail auf den Weg und soll sich dann beenden.

Die Hauptklasse:


```
public class Mailmonitor2 {
	public static String CONFIG_FILE = "src/mailmonitor2.conf";
	public static String STD_QUERY = "select ProgSessionID from MailV3 where Status = 2 order by Prioritaet desc";
	
	private Properties properties=null;

	public static void main(String[] args) {
		new Mailmonitor2();
	}
	
	public Mailmonitor2() {
		this.InitProperties();

[Stuff happens]
		
		while(true) {
			//Main Loop!
			while(ProgSessionIDs.size()>0) {
				if(Thread.activeCount()<max_threads)
					new Mailthread(ProgSessionIDs.remove(0), this.GetConnection());
				Thread.yield();
			}
			if(ProgSessionIDs.size()==0) {
				try {
					Thread.sleep(Integer.parseInt(this.properties.getProperty("Main_Sleep_Time")));
					
					//Neues Laden
					try {
						if(statement.execute())
							resultset = statement.getResultSet();
					} catch (SQLException sqle) {
						System.err.println(String.format("Fehler bei erneuten Ausführung des Statements (%s).", Mailmonitor2.STD_QUERY));
						try {
							conn.close();
						} catch (SQLException e) {}
						return;
					}
					ProgSessionID=null;
					try {
						while(resultset.next()) {
							ProgSessionID = resultset.getString("ProgSessionID");
							ProgSessionIDs.add(ProgSessionID);
						}
					} catch (SQLException sqle) {
						System.err.println(String.format("Konnte Feld 'ProgSessionID' während der Auswertung des Hauptqueries (%s) nicht extrahieren (%s).", Mailmonitor2.STD_QUERY, sqle.toString()));
						try {
							conn.close();
						} catch (SQLException e) {}
						System.exit(-2);
					}
				} catch (InterruptedException ie) {
					System.err.println(String.format("InterruptedException während Sleep des Hauptthreads: %s", ie.toString()));
					try {
						conn.close();
					} catch (SQLException e) {}
					System.exit(-4);
				}
			}
		}
	}
	
	public void InitProperties() {
[Einlesen Konfigurationsdatei]
	}
	
	public Connection GetConnection() {
		try {
			Class.forName("com.mysql.jdbc.Driver").newInstance();
		} catch (ClassNotFoundException cnfe) {
			return null;
		} catch (IllegalAccessException iae) {
			return null;
		} catch (InstantiationException ie) {
			return null;
		}
    String Connection = String.format("jdbc:mysql://%s/%s?user=%s&password=%s&autoReconnectForPools=true",this.properties.getProperty("MySQL.Host"),this.properties.getProperty("MySQL.Database"),this.properties.getProperty("MySQL.User"),this.properties.getProperty("MySQL.Password"));
    Connection conn = null;
    try {
    	conn = DriverManager.getConnection(Connection);
    } catch (SQLException sqle) {
    	return null;
    }
    
    return conn;
	}
}
```

Die Threadklasse:


```
public class Mailthread implements Runnable {
	private static String STD_QUERY = "SELECT * FROM MailV3 WHERE ProgSessionID = '%s'";
	private static String DONE_STMT = "UPDATE MailV3 SET Status = 4 WHERE ProgSessionID = '%s'";

	private String ProgSessionID;
	private Connection conn;
	private PreparedStatement statement;

	public Mailthread(String ProgSessionID, Connection conn) {
		this.ProgSessionID = ProgSessionID;
		this.conn = conn;
		try {
			this.statement = this.conn.prepareStatement(String.format(STD_QUERY, ProgSessionID));
		} catch (SQLException sqle) {
			System.err.println(String.format("Konnte Statement für Thread %s nicht vorbereiten.", Mailthread.STD_QUERY));
			return;
		}
		new Thread(this).start();
	}

	@Override
	public void run() {
		// Abfrage auf DB machen
		if (this.statement == null) {
			return;
		}

		ResultSet resultset = null;
		try {
			if(this.statement.execute())
				resultset = this.statement.getResultSet();
		} catch (SQLException e) {
			return;
		}

		if (resultset == null) {
			return;
		}
		try {
			resultset.first();
		} catch (SQLException sqle) {
			System.err.println(String.format("Keinen Datensatz für %s gefunden.", this.ProgSessionID));
			return;
		}

[Mailvorbereitung]

		try {
			Transport transport = session.getTransport("smtp");
			transport.connect();
			Transport.send(msg);
		} catch (SendFailedException sfe) {
			System.err.println(String.format("Das Versenden von Mail %s schlug fehl (%s).", this.ProgSessionID, sfe.toString()));
		} catch (MessagingException me) {
			System.err.println(String.format("Das Versenden von Mail %s schlug fehl (%s).", this.ProgSessionID, me.toString()));
			return;
		}
		
		try {
			this.statement = this.conn.prepareStatement(String.format(Mailthread.DONE_STMT, this.ProgSessionID));
		} catch (SQLException sqle) {
			System.err.println(String.format("Konnte Statement für Update des Statusfeldes für Mail %s nicht vorbereiten (%s): %s", this.ProgSessionID, String.format(Mailthread.DONE_STMT, this.ProgSessionID), sqle.toString()));
			return;
		}
		
		try {
			this.statement.execute();
		} catch (SQLException sqle) {
			System.err.println(String.format("Statement für Update des Status nach erfolgreichem Versand der Mail %s konnte nicht ausgeführt werden (%s): %s", this.ProgSessionID, String.format(Mailthread.DONE_STMT, this.ProgSessionID), sqle.toString()));
			return;
		}

		try {
			this.conn.close();
		} catch (SQLException e) {}
	}
}
```

Mein Problem dabei ist, dass die Threads nicht beendet werden. Debugge ich diese Chose, so sehe ich, dass die maximale Anzahl an Threads unter dem Mainthread stehen und sich nicht bewegen. Normalerweise würde ich erwarten, dass die run()-Methode sich beendet, dann danach der Konstruktor verlassen wird und damit der darunterliegende Thread beendet werden würde.

Leider passiert dies nicht. Ich würde ungerne zu so unsauberen Lösungen wie Thread.stop() greifen müssen.

Kann jemand einen Denkanstoß geben?

Danke.


----------



## SlaterB (22. Mrz 2010)

> Normalerweise würde ich erwarten, dass die run()-Methode sich beendet, dann danach der Konstruktor verlassen

meinst du den Konstruktor von Mailthread? der sollte sofort beendet werden, 
noch bevor die run()-Methode startet, die wird schließlich nebenläufig ausgeführt,
der Konstruktor nicht solange blockiert wie bei einem normalen Methodenaufruf

um Threads an sich kennenzulernen/ zu testen,
entferne aus deinem Code alles was mit Mail oder sonst wie exotischem zu tun hat,
in der run-Methode nur System.out.println und vielleicht Thread.sleep() zur Simulation von Dauer

damit kannst du testen, 
wenn dort was nicht klappt, dann poste ein entsprechendes einfaches Testprogramm,

ansonsten wirds wohl an deinem Mail-Code liegen,
mit System.out.println zwischen jeder einzelnen Zeile kannst du genau erkennen,
wie weit eine run-Methode abgearbeitet wurde oder wo sie stehen bleibt


----------



## André Uhres (22. Mrz 2010)

tme hat gesagt.:


> Dieser liest dann den entsprechenden Datensatz, bringt die Mail auf den Weg und soll sich dann beenden.


Ein Thread ist immer dann abgeschlossen, wenn die mit dem Thread verbundene run() Methode beendet ist. Du hast wahrscheinlich ein Datenbank- und/oder Mail-Problem, wodurch die run Methode hängen bleibt.


----------



## tme (22. Mrz 2010)

Hallo und danke für die Antworten.

Die run()-Methode wird beendet. Ein System.out.println() am Ende der Methode wird normal aufgerufen. Dasselbe im Konstruktor wird auch normal ausgeführt/angezeigt:

Ende des Konstruktors für a8262d92-275f-11df-a7da-001517a929cc erreicht.
Ende des Konstruktors für a821f1f0-275f-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für a8262d92-275f-11df-a7da-001517a929cc erreicht.
Ende des Konstruktors für a8201380-275f-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für a821f1f0-275f-11df-a7da-001517a929cc erreicht.
Ende des Konstruktors für a81e2476-275f-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für a8201380-275f-11df-a7da-001517a929cc erreicht.
Ende des Konstruktors für a81ba87c-275f-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für a81e2476-275f-11df-a7da-001517a929cc erreicht.
Ende des Konstruktors für a818a9a6-275f-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für a81ba87c-275f-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für a818a9a6-275f-11df-a7da-001517a929cc erreicht.
Ende des Konstruktors für 2c30ea26-2753-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für 2c30ea26-2753-11df-a7da-001517a929cc erreicht.
Ende des Konstruktors für 2c2fe108-2753-11df-a7da-001517a929cc erreicht.
Ende der run()-Methode für 2c2fe108-2753-11df-a7da-001517a929cc erreicht.

Komisch empfang ich jedoch, dass in der Debug-Anzeige folgendes stand:

Mailmonitor2 [Java Application]	
	mailmonitor2.Mailmonitor2 at localhost:59516	
		Thread [main] (Running)	
		Daemon Thread [Timer-0] (Running)	
		Daemon Thread [Timer-1] (Running)	
		Daemon Thread [Timer-2] (Running)	
		Daemon Thread [Timer-3] (Running)	
		Daemon Thread [Timer-4] (Running)	
		Daemon Thread [Timer-5] (Running)	
		Daemon Thread [Timer-7] (Running)	
		Daemon Thread [Timer-8] (Running)	
	C:\Program Files (x86)\Java\jre6\bin\javaw.exe (22.03.2010 11:38:02)	

Daemon-Thread? Das hatte ich doch garnicht konfiguriert. Das würde auch erklären, warum die Threads auch ohne Notwendigkeit ewig weiterlaufen. Ich habe deswegen mal folgende Änderung durchgeführt:


```
Thread t = new Thread(this);
		t.setDaemon(false);
		t.start();
```

Interessanterweise ändert dies am Verhalten nichts (und der Standardwert für Daemon ist auch false). Zumindest könnte ich mir bei dieser Anzeige also vorstellen, dass die Threads laufen, weil sie irgendwie glauben, sie müßten als Daemon laufen. Aber warum nur?

Thomas


----------



## SlaterB (22. Mrz 2010)

'Daemon Thread [Timer-0] (Running) ' bei google eingetippt führt zu Themen wie

OTN Discussion Forums : How can I enable remote debug in ...
https://jira.jboss.org/jira/browse/SOA-563

entweder der Debugger macht da irgendwas schlimmes, was aber nicht zu erwarten ist,
oder intern gibts Hintergrund-Threads, z.B. pro Connection

falls du noch vermutest, dass es die von dir gestarteten Threads sind, so kannst du zu Beginn/ Ende der run-Methode deren toString() ausgeben und mit den späteren Ausgaben der Debug-Anzeige vergleichen,
oder testweise eine eigene Thread-Klasse verwenden und toString() überschreiben


----------



## tme (22. Mrz 2010)

SlaterB hat gesagt.:


> falls du noch vermutest, dass es die von dir gestarteten Threads sind



Hallo Slater,

irgendwas hast du da falsch verstanden  Es handelt sich selbstverständlich um die von mir gestarteten Threads. Worauf ich hinauswollte, war das "Daemon Thread". Ein Daemon ist unter Linux ein Hintergrundprozeß, bei Windows wäre das sowas wie ein Dienst. Das Daemon-Flag gibt einem Thread an, ob er als Daemon im Hintergrund laufen soll oder eben nicht. Deshalb ist diese Daemon-Eigenschaft für das Verhalten der Threads sehr wichtig und würde meine Beobachtungen erklären.

Warum diese Threads allerdings als Daemon laufen (und ob ich die Anzeige überhaupt als "Flag Daemon ist für diesen Thread gesetzt" interpretieren sollte), weiß ich jedoch nicht.


----------



## SlaterB (22. Mrz 2010)

interne Threads kannst du überhaupt nicht kontrollieren,
wenn du 
> Transport.send(msg);
aufrufst (übrigens eine statische Methoden während du vorher ein Transport-Objekt erzeugst?),
dann werden vielleicht 10 Threads und 20 Daemons erzeugt, 
das ist eine BlackBox, du kannst nur beten dass alles geht oder vielleicht nach Einstellungen suchen


wenn du weißt, dass es nicht dein Thread ist, dann verstehe ich nicht, warum du
> Thread t = new Thread(this);
> t.setDaemon(false);
testest,
nur extrem kann man vermuten, dass die Methoden-Aufrufe in die BlackBox hinein
den Status des aktuellen Threads nachschauen und übernehmen oder so

-----

zum eigentlichen Thema kann ich nichts weiter beitragen :bahnhof:


----------



## tme (22. Mrz 2010)

tme hat gesagt.:


> Es handelt sich selbstverständlich um die von mir gestarteten Threads.





SlaterB hat gesagt.:


> wenn du weißt, dass es nicht dein Thread ist



Ich weiß, dass es meine Threads sind. Ich kann im Debugger beobachten, dass die Threads hinzugefügt werden, wenn ich an den entsprechenden Stellen in meinem Code bin.

Interessant ist vielleicht noch, was die Threads tun, wenn sie da so rumstehen und warten:

Daemon Thread [Timer-0] (Suspended)	
	Object.wait(long) line: not available [native method]	
	TaskQueue(Object).wait() line: 485	
	TimerThread.mainLoop() line: not available	
	TimerThread.run() line: not available	

Habe mal suspended und mir den Stacktrace angeschaut. Dass hier keine Zeile in run() angegeben ist, ist nur charakteristisch dafür, dass run() längst beendet ist. Spannend ist natürlich, dass das angegebene TaskQueue-Objekt (für welches als Einziges eine Zeilennummer angegeben ist) kein Inhalt des javax oder java-Namespaces ist.


----------



## SlaterB (22. Mrz 2010)

oh, verlesen

> Dass hier keine Zeile in run() angegeben ist, ist nur charakteristisch dafür, dass run() längst beendet ist.

das ist charakteristisch dafür, dass nichts von deinem Code beteiligt ist, die run()-Methode dieses Threads aber immer noch läuft

es ist
java.util.TimerThread

```
public void run() {
        try {
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }
```
und meiner Meinung nach immer noch ein anderer intern gestarteter Thread


----------



## Janus (22. Mrz 2010)

tme hat gesagt.:


> Zumindest könnte ich mir bei dieser Anzeige also vorstellen, dass die Threads laufen, weil sie irgendwie glauben, sie müßten als Daemon laufen.



Java unterscheidet zwischen user und daemon threads. Eine VM darf nicht beendet werden, solange noch mindestens ein user thread läuft. Bedeutet im Umkehrschluss also, dass die VM sich sofort beenden darf, wenn lediglich ein paar daemon threads laufen und diese dafür auch gern einmal abschiesst.


----------



## FArt (22. Mrz 2010)

Tipp:
Bevor ich (außer zu Lernzwecken) etwas fehlerhaftes implementiere, benutze ich lieber etwas bereits vorhandenes: java.util.concurrent ... da gibt es Scheduler, Queues, ExecutorService, das ganze mit limitierten Threads, Threadpools, usw.

Zum Code: eine Exception fangen und dann "nur" eine Fehlermeldung ausgeben und mit return beenden ist zur Fehlersuche suboptimal. Dein Code weist auch sonst kein Logging auf. Das macht (gerade Abläufe bei Multithreading) schwer nachvollziehbar. Debugging ist hier oft nicht so einfach bzw. verzerrt das Laufzeitverhalten. Du gehst sehr unsauber mit Ressouren um (Statements, Connections). Diese müssen in einem finally-Block in JEDEM Fall sicher geschlossen werden.

Annahme: wenn du diese Tipps befolgst, hat sich die Frage erledigt.


----------



## Murray (22. Mrz 2010)

1. Wenn du im Debugger schrittweise durch die run-Methode des Thread gehst, müsste doch eigentlich zu sehen sein, bei welcher Aktion ein zusätzlicher  Timer-Thread entsteht

2.

```
Transport transport = session.getTransport("smtp");
            transport.connect();
            Transport.send(msg);
```
Die ersten beiden Zeilen sind überflüssig, da du in der dritten ja die statische send-Methode verwendest, die sich selbst um das Erzeugen (und vermutlich auch um das korrekte Beenden) der Connection kümmert. Momentan erzeugst du ein Transport-Objekt, welches a) niemals zum Senden verwendet wird und b) dessen close()-Methode nicht aufgerufen wird. Letzteres könnte schon mal ein Kandidat sein, wenn das Programm irgendwelche Resourcen (wie z.B. Threads)  nicht korrekt freigibt.


----------



## tme (22. Mrz 2010)

FArt hat gesagt.:


> Bevor ich (außer zu Lernzwecken) etwas fehlerhaftes implementiere, benutze ich lieber etwas bereits vorhandenes: java.util.concurrent ... da gibt es Scheduler, Queues, ExecutorService, das ganze mit limitierten Threads, Threadpools, usw.
> 
> ...
> 
> Du gehst sehr unsauber mit Ressouren um (Statements, Connections). Diese müssen in einem finally-Block in JEDEM Fall sicher geschlossen werden.



Ich bin mir der Existenz dieser Hilfsmittel mittlerweile bewußt. Ich kannte diese noch nicht, als ich die Applikationsstruktur designed habe. Derzeit bezweifle ich einfach nur, dass diese Klassen etwas an meinem Problem ändern würden, wobei ich am Ende ggf. doch nicht um den Versuch rumkomme, einen der vorgefertigten Scheduler oder ExecutorServices zu nutzen. Was natürlich bei der Fehlersuche nicht hilft. Frustrierend sowas.

Um auszuschließen, dass eine irgendwie gesperrte Resource für mein Dilemma verantwortlich ist, habe ich jetzt bis auf die Threaderzeugung im Konstruktor alles auskommentiert. Läuft das Programm, so baut es ersteinmal Thread-0 bis Thread-4, verwirft aber mit Erzeugung Thread-5 Thread-1 bis Thread-4 und baut dann die restlichen wartenden Threads folgendermaßen auf:

Mailmonitor2 [Java Application]	
	mailmonitor2.Mailmonitor2 at localhost:60467	
		Thread [main] (Suspended)	
			Mailmonitor2.<init>() line: 75	
			Mailmonitor2.main(String[]) line: 21	
		Daemon Thread [Timer-0] (Running)	
		Daemon Thread [Timer-1] (Running)	
		Daemon Thread [Timer-5] (Running)	
		Daemon Thread [Timer-6] (Running)	
		Daemon Thread [Timer-7] (Running)	
		Daemon Thread [Timer-8] (Running)	
		Daemon Thread [Timer-9] (Running)	
		Daemon Thread [Timer-10] (Running)	
	C:\Program Files (x86)\Java\jre6\bin\javaw.exe (22.03.2010 13:27:28)	

Scheint also nicht an irgendeinem Lock zu liegen, wobei das nachvollziehbare Verhalten der Threadnummern schon ein wenig komisch ist.


----------



## Murray (22. Mrz 2010)

tme hat gesagt.:


> Um auszuschließen, dass eine irgendwie gesperrte Resource für mein Dilemma verantwortlich ist, habe ich jetzt bis auf die Threaderzeugung im Konstruktor alles auskommentiert.


Ist bei diesem Test noch etwas in der run()-Methode geblieben, oder macht der Thread nichts mehr?


----------



## FArt (22. Mrz 2010)

tme hat gesagt.:


> Um auszuschließen, dass eine irgendwie gesperrte Resource für mein Dilemma verantwortlich ist,


Das muss nicht sein, ich habe nur gesehen, dass das zumindes ein zukünftiges Problem werden könnte.

Wichtiger ist ein sauberes (threadbezogenes) Logging mit einer Logging-API deiner Wahl. Ich nehme an, dass sich dann sehr schnell herauslesen lässt, wie es zu deinem Problem kommt.


----------



## tme (22. Mrz 2010)

Murray hat gesagt.:


> Ist bei diesem Test noch etwas in der run()-Methode geblieben, oder macht der Thread nichts mehr?



Der Thread tut nichts mehr. Übrig sind ein (leerer) Konstruktor und die run()-Methode, die ebenfalls keinen Code beinhaltet.


----------



## tme (22. Mrz 2010)

FArt hat gesagt.:


> Das muss nicht sein, ich habe nur gesehen, dass das zumindes ein zukünftiges Problem werden könnte.
> 
> Wichtiger ist ein sauberes (threadbezogenes) Logging mit einer Logging-API deiner Wahl. Ich nehme an, dass sich dann sehr schnell herauslesen lässt, wie es zu deinem Problem kommt.



Das richtige Tool zur richtigen Zeit.

Sicherlich handelt es sich bei Logging-Frameworks um das richtige Tool, aber die Zeit, um für ein kleines Projekt ein größeres Logging-Framework anzubinden, ist es derzeit nicht. Die Einarbeitung würde ggf. den Rahmen der Zeitplanung für dieses Projekt sprengen, besonders deshalb, weil man genauso ja für ein Datenbankzugriffsframework argumentieren könnte.

Und so fand ich die Lösung von Catalina nicht übel. Man überläßt das Logging dem System.out.println und System.err.println und Logrotate erledigt den Rest.


----------



## tme (22. Mrz 2010)

So. Ich habe eine neues Projekt erzeugt und den Code 1:1 übernommen und immer weiter verschlankt. Am Ende trat das Problem nicht mehr auf, wenn ich den Zugriff auf MySQL entferne.

Ich habe im Debugging bestätigt, dass die Verbindung definitiv ohne Exception immer geschlossen wird. Trotzdem bleibt der entsprechende Daemon Thread hängen.


----------



## SlaterB (22. Mrz 2010)

bringt
 Class.forName("com.mysql.jdbc.Driver");
statt
 Class.forName("com.mysql.jdbc.Driver").newInstance();
etwas?
in jedem Fall muss das nur einmal geschehen, raus aus der while-Schleife nur einmal am Anfang aufrufen


----------



## FArt (22. Mrz 2010)

tme hat gesagt.:


> Das richtige Tool zur richtigen Zeit.
> 
> Sicherlich handelt es sich bei Logging-Frameworks um das richtige Tool, aber die Zeit, um für ein kleines Projekt ein größeres Logging-Framework anzubinden, ist es derzeit nicht. Die Einarbeitung würde ggf. den Rahmen der Zeitplanung für dieses Projekt sprengen, besonders deshalb, weil man genauso ja für ein Datenbankzugriffsframework argumentieren könnte.
> 
> Und so fand ich die Lösung von Catalina nicht übel. Man überläßt das Logging dem System.out.println und System.err.println und Logrotate erledigt den Rest.



Es geht nicht darum zwingend ein Logging-Framework zu verwenden, sonder sinnvolles Logging einzubauen. Der richtige Zeitpunkt ist: spätestens jetzt.
Sytem.outs muss ich halt wieder ausbauen und Logmeldungen lass ich sinnvollerweise drin... kann man immer mal brauchen...

Natürlich kann man auch weiter raten, was das wohl passiert. Meine Behauptung von oben war: baue sinnvolles Logging ein und du wirst den Fehler finden.


----------



## tme (22. Mrz 2010)

FArt hat gesagt.:


> Natürlich kann man auch weiter raten, was das wohl passiert. Meine Behauptung von oben war: baue sinnvolles Logging ein und du wirst den Fehler finden.



Sicherlich ist dies ein guter Default-Standpunkt.

In jedem Falle habe ich das Problem idetifiziert: Jeder Aufruf von


```
conn = DriverManager.getConnection();
```

erzeugt einen dieser Daemon-Threads. Diese liegen in derselben Threadgroup (main) und bleiben anscheinend offen, auch wenn man explzit close() aufruft. Ich habe keine Methode gefunden, die auf einen anderen, alternativen Abschlußmechanismus schließen läßt.

Das Programm ist jetzt so verändert, dass es nur noch eine Verbindung nutzt, welche an alle Threads herumgereicht wird und in den Threads nur noch mit Samtpfoten (spricht: synchronized) angefaßt wird. Anscheinend funktioniert damit der Versand.

Ich danke den Anwesenden für die Hilfe.

@FArt: Hast du mal einen Link oder weiterführende Hinweise, was unter "sinnvolles Logging" zu verstehen ist, wenn es eben nicht ein Loggingframework sein soll?

Danke,

Thomas


----------



## Sonecc (22. Mrz 2010)

Sinnvolles Logging beinhaltet z.B.: Uhrzeit, Klassenname in der die Meldung passiert. Status (Info, Warn, Error) usw.

System.out und System.err sind einfach völlig nutzlos um Fehler zu identifizieren oder einzugrenzen

Logging Systeme bringen auch nicht zwingend viel Overhead mit oder große einarbeitung.
Die eingebaute Logging Möglichkeit über java.util.logging ist meist schon genug und verrichtet seinen Dienst.


----------



## FArt (22. Mrz 2010)

Das Für und Wider eines Logging-Frameworks gegenüber System.out wird an vielen Stellen diskutiert und leuchtet auch schnell ein.
Sinnvolles Logging aber bedeutet, an den richtigen Stellen die richtigen Informationen zu loggen, sonst nichts. Über das Logfile kannst du das Laufzeitverhalten deiner Applikation einsehen. Welcher Thread macht wann was und wann macht er vermeintlich nichts.... ziemlich einfaches Hilfsmittel und Programmieren verliert vieles an Magie.



> In jedem Falle habe ich das Problem idetifiziert: Jeder Aufruf von
> conn = DriverManager.getConnection();
> erzeugt einen dieser Daemon-Threads. Diese liegen in derselben Threadgroup (main) und bleiben anscheinend offen, auch wenn man explzit close() aufruft. Ich habe keine Methode gefunden, die auf einen anderen, alternativen Abschlußmechanismus schließen läßt.


???


----------

