Halli Hallo
Der Plan ist, den “Cancel Current …”/“Terminate” Button aus Eclipse nachzubauen. Dazu hab ich mir ein Task-Objekt gebaut welches die Aktion ausführt, und einen Scheduler welcher diese Tasks aufnimmt und in einen Executor schmeißt. Soweit funktioniert das auch alles. Nun sollte noch die Komponente des abbrechens hinzukommen. Sprich wenn eine Aktion zu lange dauert kann der Nutzer selber entscheiden das nun abgebrochen wird. Das da eine Meldung etc. pp auftauen kann/muss ist hier erstmal außen vor.
Aktuell sieht es verkürzt wie folgt aus:
Task-Klasse
public class Task extends Thread{
private int taskCounter = 0;
private int id;
private String descr;
public Task(String descr){
this.descr = descr;
this.id = counter++;
}
public int getID(){
return id;
}
}
Scheduler
public class TaskScheduler extends Thread{
private static final TaskScheduler instance = new TaskScheduler ();
private static ExecutorService workingService;
private static Task currentTask;
private static Queue<Task> taskQueue;
private static HashMap<Long, Task> taskMap;
public TaskScheduler(){
taskQueue = new LinkedList<Task>();
workingService = Executors.newCachedThreadPool();
taskMap = new HashMap<Integer, Task>();
this.start();
}
public static void addTask(Task task){
taskQueue.offer(task);
}
public static void cancelTask(int id){
taskMap.get(id).interrupt();
}
public void run(){
while(taskQueue.size() > 0){
currentTask = taskQueue.poll();
taskMap.put(currentTask.getID(), currentTask);
workingService.execute(currentTask);
}
try {
this.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Haupt-Klasse
public void main(){
Task t = new Task("Task 1"){
public void run(){
this.sleep(10000);
}
}
TaskScheduler.addTask(t);
TaskScheduler.cancelTask(t.getID());
}
Irgendwie erscheint mir dieses Konstrukt aber eher fragwürdig. Was sagen die Experten denn zu dem Thema, wie man das lösen kann/sollte? Bisher habe ich mich mit Threads eher zurückgehalten wenn es darum ging sich damit tiefer zu beschäften (Executor Möglichkeiten bspw.)
Das ganze static-Zeux sollte wohl erstmal raus. Dann gibt es noch einige … “Inkonsistenzen”
static HashMap<Long, Task> taskMap;
...
taskMap = new HashMap<Integer, Task>();
Aber wichtiger sind wohl erstmal andere Punkte:
- Man braucht diese Map vermutlich nicht
- Man braucht auch die Queue vermutlich nicht. Die entsprechenden ExecutorServices haben schon ihre eigene Queue.
- Task sollte NICHT von Thread erben, sondern nur ein Runnable sein. Die Threads verwaltet der ExecutorService schon intern. Damit hat man selbst meist nichts mehr zu tun.
Das canceln einzelner Tasks kann man dann woh am einfachsten erreichen, indem man nicht workingService.execute(currentTask)
verwendet, sondern
Future<?> future = workingService.submit(currentTask);
Das Future hat dann eine Methode cancel(boolean mayInterruptIfRunning)
.
Vielleicht noch zur Queue und dem Erben von ‘Thread’: Es scheint, als hättest du versucht, da genau das nachzubauen, was der ExecutorService schon macht. Man kann z.B. einen ExecutorService erstellen, der INTERN (!) 4 Threads hat. Dann kann man ihm 100 Runnables geben (also 100 Tasks). Und sein Job ist es dann, diese 100 Runnables in eine Queue zu legen, und so lange von dieser Queue Tasks runterzunehmen und den 4 Threads zuzuschanzen, bis die Queue leer ist.
Erstmal danke für die Antwort, hilft schonmal etwas weiter.
Ja das mit den Inkonsestenzen lag wohl am abtippen. Im Code ist das dann natürlich nicht mehr. Was ich mit der eigenen Map erreichen wollte ist, das ich ja beim canceln Task X brauche. Selbst wenn ich nun einen Service mit 4 Threadplätzen habe und ich will den 3. Abbrechen, muss ich ja wissen welcher Task das ist. Ohne das ganze jetzt getestet zu haben, woher weiß ich denn in dem Service welchen Task in ansprechen muss? Wenn ich das so wie oben baue werden verfügbare Tasks ausgeführt bzw. in eine Queue gepackt. Wenn die aber per execute/submit ausgeführt werden kommt u.U. ein neuer Task an die Reihe, und wird einem Threadplatz zugewiesen. Damit hab ich doch aber den vorherigen verloren, oder seh ich das falsch?
Also im kurzen: Ich muss selbstständig merken welche Threads gerade vom Service ausgeführt werden um dann zu gucken welchen ich abbreche. Werde dasganze aber nochmal in einem Beispiel nachvollziehen und in die API gucken.
Aber eine allg. Frage: Wie würden die Profis denn dieses Eclipse Feature nachbauen? Sprich Threads, welche jeweils für sich abbrechbar sind .D
Hi,
die Implementierung hängt natürlich von vielen Faktoren ab.
Grundsätzlich kannst oder sollst du keine Threads „abschießen“. Was du machen kannst ist diese sich selbstständig beenden.
Hierzu muss jeder Thread ein Flag pollen „isInterrupted()“. Demzufolge kann man nur diejenigen Dienste abbrechen, die mehrere Schritte haben. Aber andere lassen sich auch in Eclipse nicht beenden.
Gruß,
Martin
Ich hatte mal etwas gebastelt, das dem Eclipse-Verhalten nicht unähnlich war (ist aber leider closed source). Das kann recht kompliziert werden, wenn man zwischen Modalen und nicht-Modalen Tasks unterscheiden will, und zwischen abbrechbaren und nicht-abbrechbaren, und es dann noch Tasks gibt, die “Child-Tasks” erstellen (und die Frage, wie die Modalität und die Abbrechbarkeit sich auf die übertragen - z.B. “Soll das Parent gecancelt werden, wenn man ein Child cancelt?”).
Je nachdem, wie “groß” und “mächtig” du das ganze machen willst, sollte man sich da schon genau Gedanken drüber machen.
Ich vermute (!), das, was du mit der Map erreichen wolltest, kann schon über die Futures abgebildet werden. Man schickt dem ExecutorService einen Task (also ein Runnable oder Callable), mit der submit-Methode. Dann erhält man ein Future, das GENAU diesem Task entspricht. Und dabei ist erstmal egal, ob der Task schon bearbeitet wird, oder noch unbearbeitet in der Queue liegt: Wenn man auf diesem Future f.cancel(true);
aufruft, wird dafür gesorgt, dass der Task nicht mehr ausgeführt wird (falls er noch in der Queue lag) oder unterbrochen wird (falls er schon in Bearbeitung ist).
Wenn man da die oben angedeutete, etwas mächtigere Infrastruktur drumrumbauen will, muss man sich aber ggf. schon ein bißchen mehr durch die Details des ExecutorServices wühlen. Man kann dort an vielen Stellen eingreifen, z.B. in AbstractExecutorService bestimmte Methoden überschreiben (beforeExecute und afterExecute) oder auch beeinflussen, welche konkreten Implementierungen des “Future”-Interfaces intern erstellt werden… Die Frage ist, WAS damit alles WIE möglich sein soll…