T
tuxedo
Gast
Hallo zusammen,
ich hab hier ne Klasse mit der ich kleine Tasks in eine Liste Stecke und die von einem Thread sequentiell abgearbeitet wird.
Ich nutze die Klasse hauptsächlich um Events zu entkoppeln: Ein Event wird ausgelöst, im Eventhandler gefangen und eine entsprechende Aktion getriggert. Diese Aktion soll aber nicht aus dem Thread heraus gestartet werden die das Event getriggert hat. Und um das zu entkoppeln benutze ich obige Klasse.
Prinzipiell funktioniert das prima. Aber im Dauertest versagt das ganze:
Die Anwendung läuft das ganze Wochenende über. Pro 30sek werden in etwa 8-16 Events bzw. Tasks mit dieser Klasse "verarbeitet". Also nicht wirklich viel.
Aus bisher ungeklärten Gründen beendet sich der TaskProcessor Thread "einfach so", ohne Exception, ohne Fehler ???:L Und das ist natürlich verheerend. Denn unter anderem müssen auf viele Events Aufräumarbeiten ausgeführt werden die Threadpools abräumen, Netzwerkverbindungen schließen etc. etc... Ohne den TaskProcessor stirbt die Anwendung recht schnell. ;(
Ich könnte nun anfangen am Code rumzuschrauben und mit try&error den Fehler suchen. Aber ich will das Problem vorher in kleinerem Rahmen reproduzieren damit ich weiß wo's klemmt. Andernfalls muss ich immer >8h warten bis der Fehler in der Anwendung auftritt.
Also hab ich eine Test-Klasse gebastelt die 500 Threads erzeugt. Jeder Thread fügt 100 Tasks dem ThreadProcessor hinzu. Mit genug Heap läuft das Problemlos. Keine Exception, nix. Läuft prima. Kein Fehler.
Da ich das entfernen von Tasks aus der Liste nicht synchronisiert habe, hätte ich erwartet dass meine Testklasse eine "ConcurrentModificationException" hervorbringt. Aber nix dergleichen. Läuft problemlos.
Hier mal noch die Testklasse:
Ich hab das Gefühl ich seh den Wald vor lauter Bäumen nicht mehr. Sieht jemand spontan nen Fehler? Oder hat jemand die Muse ne Testklasse zu basteln mit der das Problem reproduzierbar ist?
Gruß
Alex
ich hab hier ne Klasse mit der ich kleine Tasks in eine Liste Stecke und die von einem Thread sequentiell abgearbeitet wird.
Java:
import java.util.ArrayList;
import java.util.List;
import foundation.logging.Logger;
public class TaskProcessor extends Thread {
private static Logger mLogger = Logger.getLogger(TaskProcessor.class);
// instantiates the taskprocessor on class-load via classloader
private static final TaskProcessor processor = new TaskProcessor();
// the list with tasks that have to be executed asynchronously
private static List<Task> taskList = new ArrayList<Task>();
/**
* Starts the TaskProcessor
*
*/
private TaskProcessor()
{
setName("TaskProcessor");
setDaemon(true);
start();
}
/**
* @see java.lang.Thread#run()
*/
@Override
public void run ()
{
mLogger.info("TaskProcessor running ...");
while (true) { // this is a daemon thread, so it ends when JVM terminates...
if (taskList.isEmpty()) {
synchronized (taskList)
{
try
{
taskList.wait(1000);
}
catch (InterruptedException anException)
{
// nothing to do
}
}
} else {
Task first = taskList.remove(0);
mLogger.trace("Calling task [",first.getTaskName(),"]");
long start = System.currentTimeMillis();
try {
first.run();
} catch (Exception e) {
StringBuffer sb = new StringBuffer();
StackTraceElement[] stackTrace = e.getStackTrace();
for (int i=0; i<stackTrace.length; i++){
sb.append("\tat ");
sb.append(stackTrace[i].getClassName());
sb.append(".");
sb.append(stackTrace[i].getMethodName());
sb.append("(");
sb.append(stackTrace[i].getFileName());
sb.append(":");
sb.append(stackTrace[i].getLineNumber());
sb.append(")\n");
}
mLogger.error("Exception occured while running task [",first.getTaskName(),"] on "+
"TaskProcessor: class: ",e.getClass()," msg: ",e.getMessage(),"\n",sb.toString());
}
long end = System.currentTimeMillis();
mLogger.trace("task [",first.getTaskName(),"] done. duration=",(end-start)," ms.");
}
}
// taskList.clear();
// mLogger.info("TaskProcessor shutting down ...");
}
/**
* Executes a given {@link Runnable} in the taskprocessor
*
* @param runnable the runnable to execute asynchronously
* @param name a name that is used to track the duration in the logs
*/
public static void execute(Runnable runnable, String name) {
mLogger.trace("execute(): enqueuing task [",name,"] ...");
synchronized (taskList)
{
taskList.add(new Task(runnable, name));
taskList.notify();
}
mLogger.trace("execute(): enqueuing task [",name,"] ... *DONE* ");
}
}
Ich nutze die Klasse hauptsächlich um Events zu entkoppeln: Ein Event wird ausgelöst, im Eventhandler gefangen und eine entsprechende Aktion getriggert. Diese Aktion soll aber nicht aus dem Thread heraus gestartet werden die das Event getriggert hat. Und um das zu entkoppeln benutze ich obige Klasse.
Prinzipiell funktioniert das prima. Aber im Dauertest versagt das ganze:
Die Anwendung läuft das ganze Wochenende über. Pro 30sek werden in etwa 8-16 Events bzw. Tasks mit dieser Klasse "verarbeitet". Also nicht wirklich viel.
Aus bisher ungeklärten Gründen beendet sich der TaskProcessor Thread "einfach so", ohne Exception, ohne Fehler ???:L Und das ist natürlich verheerend. Denn unter anderem müssen auf viele Events Aufräumarbeiten ausgeführt werden die Threadpools abräumen, Netzwerkverbindungen schließen etc. etc... Ohne den TaskProcessor stirbt die Anwendung recht schnell. ;(
Ich könnte nun anfangen am Code rumzuschrauben und mit try&error den Fehler suchen. Aber ich will das Problem vorher in kleinerem Rahmen reproduzieren damit ich weiß wo's klemmt. Andernfalls muss ich immer >8h warten bis der Fehler in der Anwendung auftritt.
Also hab ich eine Test-Klasse gebastelt die 500 Threads erzeugt. Jeder Thread fügt 100 Tasks dem ThreadProcessor hinzu. Mit genug Heap läuft das Problemlos. Keine Exception, nix. Läuft prima. Kein Fehler.
Da ich das entfernen von Tasks aus der Liste nicht synchronisiert habe, hätte ich erwartet dass meine Testklasse eine "ConcurrentModificationException" hervorbringt. Aber nix dergleichen. Läuft problemlos.
Hier mal noch die Testklasse:
Java:
public class TaskProcessorStressTest
{
public static void main (String[] args) throws InterruptedException
{
Thread.sleep(10000); // time needed to attach jconsole
System.out.println("Start");
final int threadCount = 500;
final int taskCount = 100;
Thread[] threads = new Thread[threadCount];
// simple "final counter" class
class Count {
public volatile int i = 0;
}
final Count counter = new Count();
for (int t=0; t<threadCount;t++) {
final int tt = t;
threads[t] = new Thread() {
/**
* @see java.lang.Thread#run()
*/
@Override
public void run ()
{
for(int i=0; i<taskCount; i++){
final int num = i;
TaskProcessor.execute(new Runnable(){
@Override
public void run ()
{
System.out.println("Doing something#"+num+" on "+Thread.currentThread().getName());
counter.i++;
}
}, "Runnable#"+i);
}
System.out.println("Worker#"+tt+" terminated");
}
};
}
for (int t=0; t<threadCount;t++) {
threads[t].setName("Worker#"+t);
threads[t].start();
}
Thread.sleep(120000); // waiting for completion
System.err.println("total: "+counter.i); // should be taskCount*threadCount ...
}
}
Ich hab das Gefühl ich seh den Wald vor lauter Bäumen nicht mehr. Sieht jemand spontan nen Fehler? Oder hat jemand die Muse ne Testklasse zu basteln mit der das Problem reproduzierbar ist?
Gruß
Alex
Zuletzt bearbeitet von einem Moderator: