diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java index eebddd6..9f0b7c5 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java @@ -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(); + }); } /**