Fachlicher Vertrag: unverändert zu erhalten
- Task-Ergebnisse, Timeout- und Abbruchsemantik sind definiert.
- Executors/Threads werden zuverlässig geschlossen.
- Fehler eines Tasks gehen nicht verloren.
plattformgebundener Thread Pool wird nachvollziehbar in Virtual Threads und sichere Aggregation überführt. Die Akte zeigt nicht nur das Ziel, sondern die tatsächlichen Dateien, APIs, Dependencies, Codebelege, Tests, Risiken und Cutover-Schritte.
| Dimension | Legacy | Modern | Migrationskonsequenz |
|---|---|---|---|
| Programmiermodell | imperativ / ohne Framework-Annotationen | explizite Java-Abstraktionen | Annotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen. |
| Abhängigkeiten | 1 direkte Dependencies | 1 direkte Dependencies | 0 neu, 0 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen. |
| Öffentliche API | 1 erkannte Methoden | 1 erkannte Methoden | Methoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag. |
| Datenmodell | class LegacyTaskRunner | class VirtualThreadTaskRunner | Feldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet. |
| Fehlerverhalten | Legacy-Exceptions und Rückgabewerte | explizitere Fach-/Framework-Fehlerabbildung | Fehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden. |
| Tests | Bestands- und Golden-Master-Tests | Unit-, Slice-, Contract- und Integrationstests | Der Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt. |
| Betrieb | Legacy-Start/Lifecycle | modernes Packaging, Health und externe Konfiguration | Umstellung über kleine Adapter und Golden-Master-Tests statt Big Bang. |
| Rollback | Legacy-Artefakt bleibt unverändert | Modern-Artefakt getrennt deploybar | Kein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein. |
race conditions, leaks and nondeterministic tests
timeouts, cancellation, interruption and repeated stress tests
Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.
| Legacy-Rolle / Datei | Modern-Rolle / Datei | Bedeutung |
|---|---|---|
| LegacyTaskRunner.java | VirtualThreadTaskRunner.java | Fester Pool wird für blockierende Tasks durch Virtual Threads ersetzt. |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| LegacyTaskRunner.java | class LegacyTaskRunner | — | List<String> run(List<Callable<String>> tasks) | Task Executor | 24 |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| VirtualThreadTaskRunner.java | class VirtualThreadTaskRunner | — | List<String> run(List<Callable<String>> tasks) | Task Executor | 22 |
| Status | Dependency | Version | Scope | Prüfung |
|---|---|---|---|---|
| BEIBEHALTEN | org.junit.jupiter:junit-jupiter | BOM/Parent → BOM/Parent | test → test | Gemeinsame Dependency; Version und Scope im effektiven POM prüfen. |
Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.
Migrationsbedeutung: Fester Pool wird für blockierende Tasks durch Virtual Threads ersetzt.
projects/16-concurrency/legacy/src/main/java/at/aydin/lab/concurrency/legacy/LegacyTaskRunner.java
package at.aydin.lab.concurrency.legacy;
import java.util.*;
import java.util.concurrent.*;
// Design Pattern: Task Executor
// Zweck: Begrenzter Plattform-Thread-Pool führt blockierende Aufgaben aus.
public final class LegacyTaskRunner {
public List<String> run(List<Callable<String>> tasks) {
ExecutorService pool = Executors.newFixedThreadPool(4);
try {
List<String> result = new ArrayList<>();
for (Future<String> f : pool.invokeAll(tasks)) result.add(f.get());
return result;
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(ex);
} catch (ExecutionException ex) {
throw new IllegalStateException(ex.getCause());
} finally {
pool.shutdown();
}
}
}
package at.aydin.lab.concurrency.modern;
import java.util.*;
import java.util.concurrent.*;
// Design Pattern: Task Executor
// Zweck: Ein Virtual Thread pro blockierender Aufgabe vereinfacht skalierbare Nebenläufigkeit.
public final class VirtualThreadTaskRunner {
public List<String> run(List<Callable<String>> tasks) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = tasks.stream().map(executor::submit).toList();
List<String> result = new ArrayList<>(futures.size());
for (Future<String> future : futures) result.add(future.get());
return List.copyOf(result);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(ex);
} catch (ExecutionException ex) {
throw new IllegalStateException(ex.getCause());
}
}
}
--- LegacyTaskRunner.java
+++ VirtualThreadTaskRunner.java
@@ -1,24 +1,22 @@
-package at.aydin.lab.concurrency.legacy;
+package at.aydin.lab.concurrency.modern;
import java.util.*;
import java.util.concurrent.*;
// Design Pattern: Task Executor
-// Zweck: Begrenzter Plattform-Thread-Pool führt blockierende Aufgaben aus.
-public final class LegacyTaskRunner {
+// Zweck: Ein Virtual Thread pro blockierender Aufgabe vereinfacht skalierbare Nebenläufigkeit.
+public final class VirtualThreadTaskRunner {
public List<String> run(List<Callable<String>> tasks) {
- ExecutorService pool = Executors.newFixedThreadPool(4);
- try {
- List<String> result = new ArrayList<>();
- for (Future<String> f : pool.invokeAll(tasks)) result.add(f.get());
- return result;
+ try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
+ List<Future<String>> futures = tasks.stream().map(executor::submit).toList();
+ List<String> result = new ArrayList<>(futures.size());
+ for (Future<String> future : futures) result.add(future.get());
+ return List.copyOf(result);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(ex);
} catch (ExecutionException ex) {
throw new IllegalStateException(ex.getCause());
- } finally {
- pool.shutdown();
}
}
}
| ID | Ebene | Prüfung | erforderlicher Nachweis |
|---|---|---|---|
| 16-CONCURRENCY-A01 | Integration/Contract | Kein Thread-/Executor-Leak nach wiederholten Läufen. | Automatisierter Test und CI-Protokoll |
| 16-CONCURRENCY-A02 | Integration/Contract | Timeout beendet offene Tasks nach Regel. | Automatisierter Test und CI-Protokoll |
| 16-CONCURRENCY-A03 | Integration/Contract | Unterbrechung wird nicht verschluckt. | Automatisierter Test und CI-Protokoll |
| 16-CONCURRENCY-A04 | Integration/Contract | Ergebnisse sind vollständig und korrekt zugeordnet. | Automatisierter Test und CI-Protokoll |
| 16-CONCURRENCY-A05 | Integration/Contract | Lasttest zeigt keine unkontrollierte externe Ressourcenüberlastung. | Automatisierter Test und CI-Protokoll |
| 16-CONCURRENCY-T01 | Unit | Fachlogik ohne Container oder externen Dienst testen. | Unit-Test |
| 16-CONCURRENCY-T02 | Regression | Legacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen. | Vergleichsreport |
| 16-CONCURRENCY-T03 | Negative | Fehlerhafte, leere und grenzwertige Eingaben prüfen. | Negativtest |
| 16-CONCURRENCY-T04 | Operations | Start, Health, Shutdown und Konfigurationsfehler prüfen. | Deployment-/Startprotokoll |
| 16-CONCURRENCY-F01 | Fokus | timeouts, cancellation, interruption and repeated stress tests | Modulspezifischer Testreport |
| Risiko | Auswirkung | Gegenmaßnahme | Gate |
|---|---|---|---|
| race conditions, leaks and nondeterministic tests | NIEDRIG | timeouts, cancellation, interruption and repeated stress tests | vor Cutover |
Keine zusätzlichen Ressourcen.
Der TaskRunner-Vertrag erlaubt Rückschaltung auf den bisherigen Executor; gemeinsam genutzter Zustand muss in beiden Varianten gleich geschützt sein.