Fix #53: Konfigurations-Oeffnen ueber Single-Thread-Executor serialisieren

Statt fuer jede openConfigurationFile-Anfrage einen neuen Thread zu
starten, werden Anfragen jetzt ueber einen Single-Thread-ExecutorService
mit Daemon-ThreadFactory eingereiht. Mehrfaches Klicken auf Oeffnen
erzeugt keine konkurrierenden Worker-Threads mehr; Anfragen werden
seriell hintereinander abgearbeitet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 06:21:05 +02:00
parent 8ca6d08133
commit a87c73401b
@@ -8,6 +8,8 @@ import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.prefs.Preferences;
@@ -169,6 +171,20 @@ public final class GuiConfigurationEditorWorkspace {
private final GuiConfigurationFileLoader configurationFileLoader;
private final GuiConfigurationFileWriter configurationFileWriter;
/**
* Serialisiert Lade-Auftraege fuer Konfigurationsdateien, sodass mehrere schnell
* aufeinanderfolgende Oeffnen-Aktionen (z.B. Doppelklick) keine konkurrierenden
* Worker-Threads erzeugen. Der Executor ist einzel-threadig: Auftraege werden der
* Reihe nach abgearbeitet. Der zugrundeliegende Thread ist als Daemon konfiguriert,
* damit die JVM beim Beenden nicht blockiert wird.
*/
private final ExecutorService configLoaderExecutor = Executors.newSingleThreadExecutor(runnable -> {
Thread thread = new Thread(runnable, "gui-config-loader");
thread.setDaemon(true);
return thread;
});
/**
* The current editor state. Package-private to allow direct state injection in smoke tests
* that need to set a specific dirty state without going through the full load/save pipeline.
@@ -871,7 +887,9 @@ public final class GuiConfigurationEditorWorkspace {
return;
}
Thread worker = new Thread(() -> {
// Lade-Auftrag wird seriell ueber den Single-Thread-Executor eingereicht, um
// Race Conditions durch gleichzeitig laufende Lade-Threads zu vermeiden.
configLoaderExecutor.submit(() -> {
try {
GuiConfigurationEditorState loadedState = configurationFileLoader.load(configFilePath);
// Speichern des Pfads als letzte geladene Konfiguration
@@ -881,9 +899,7 @@ public final class GuiConfigurationEditorWorkspace {
Platform.runLater(() -> showError("Konfiguration konnte nicht geladen werden: "
+ safeMessage(exception)));
}
}, "gui-config-loader");
worker.setDaemon(true);
worker.start();
});
}
/**