Zum Hauptinhalt springen

English + German

English stays on the page. Click the button to show the German text under each question.

Concurrency and JVM

Backend services are concurrent the moment they accept two HTTP requests. Interviewers use threads, volatile, and GC to see if you have shipped production bugs.

Deutsch

Concurrency und JVM

Backend-Services sind concurrent, sobald zwei HTTP-Requests reinkommen. Interviewer nutzen Threads, volatile und GC, um zu sehen, ob du schon Produktionsbugs gebaut hast.

1. Process vs thread? What does a Spring Boot app run?

Deutsch

Prozess vs. Thread? Was läuft in einer Spring-Boot-App?

Level: Junior · Listen for: one JVM process, many request threads

Niveau: Junior · Darauf hören sie: ein JVM-Prozess, viele Request-Threads

Model answer

A process has its own address space. A thread shares the heap of its process but has its own stack and program counter.

A Spring Boot web app is typically one JVM process with a pool of request threads (Tomcat/Jetty/Netty), plus extra pools for @Async, scheduling, and the GC. Two requests can touch the same singleton beans at the same time — that is why those beans must be thread-safe.

Follow-ups

  • Stack vs heap?
  • What is a daemon thread?

Trap: “Tomcat is a separate process from Spring” in an embedded Boot app.

Memory sentence: One JVM, many threads, shared singleton beans.

Musterantwort

Ein Prozess hat seinen eigenen Adressraum. Ein Thread teilt sich den Heap seines Prozesses, hat aber eigenen Stack und Program Counter.

Eine Spring-Boot-Web-App ist typischerweise ein JVM-Prozess mit einem Pool von Request-Threads (Tomcat/Jetty/Netty), plus zusätzliche Pools für @Async, Scheduling und den GC. Zwei Requests können dieselben Singleton-Beans gleichzeitig anfassen — deshalb müssen diese Beans thread-safe sein.

Nachfragen

  • Stack vs. Heap?
  • Was ist ein Daemon-Thread?

Falle: „Tomcat ist ein eigener Prozess, getrennt von Spring“ — in einer embedded Boot-App.

Merksatz: Eine JVM, viele Threads, geteilte Singleton-Beans.

2. synchronized vs ReentrantLock vs volatile?

Deutsch

synchronized vs. ReentrantLock vs. volatile?

Level: Mid · Listen for: mutual exclusion vs visibility; locks have extra features

Niveau: Mid · Darauf hören sie: mutual exclusion vs. Visibility; Locks haben zusätzliche Features

Model answer

synchronized is a built-in monitor: mutual exclusion and a memory barrier. Keep the lock on a private final object, not on this of a public class, and keep the critical section tiny.

ReentrantLock adds try-lock, timed lock, interruptible lock, and optional fairness. Use it when synchronized cannot express the wait policy. Always unlock() in finally.

volatile is visibility and ordering, not atomic compound actions. A volatile boolean running is fine as a stop flag. volatile int c; c++ is still a race.

Follow-ups

  • What does “reentrant” mean?
  • synchronized method vs block?

Trap: using volatile as if it were a lock.

Memory sentence: Locks exclude; volatile publishes; neither makes ++ atomic.

Musterantwort

synchronized ist ein eingebauter Monitor: mutual exclusion und eine Memory Barrier. Den Lock auf ein privates final-Objekt legen, nicht auf this einer public Klasse, und die Critical Section klein halten.

ReentrantLock bringt try-lock, timed lock, interruptible lock und optionale Fairness. Nimm ihn, wenn synchronized die Wait-Policy nicht ausdrücken kann. Immer unlock() in finally.

volatile ist Visibility und Ordering, keine atomaren zusammengesetzten Aktionen. Ein volatile boolean running ist ok als Stop-Flag. volatile int c; c++ ist trotzdem eine Race.

Nachfragen

  • Was heißt „reentrant“?
  • synchronized Methode vs. Block?

Falle: volatile benutzen, als wäre es ein Lock.

Merksatz: Locks schließen aus; volatile macht sichtbar; keines macht ++ atomar.

3. What is the happens-before relationship? Why do people still see stale data?

Deutsch

Was ist die happens-before-Beziehung? Warum sieht man trotzdem stale Daten?

Level: Senior · Listen for: visibility, not just “CPU cache”; safe publication

Niveau: Senior · Darauf hören sie: Visibility, nicht nur „CPU-Cache“; Safe Publication

Model answer

The Java Memory Model defines happens-before: if A happens-before B, B is guaranteed to see A’s writes. Sources include thread start/join, unlocking a monitor then locking it, writing a volatile then reading it, and completing a constructor for final fields (safe publication).

Without a happens-before edge, another thread can see a partially constructed object or an old field forever. That is why double-checked locking needs volatile on the instance, and why publishing a HashMap from a starter thread to request threads needs a safe publish (volatile, lock, or a concurrent structure).

Follow-ups

  • Why are final fields special after the constructor returns?
  • Is Thread.sleep a memory barrier? (not one you should rely on)

Trap: “volatile flushes to RAM” as the whole story.

Memory sentence: If there is no happens-before, there is no visibility guarantee.

Musterantwort

Das Java Memory Model definiert happens-before: wenn A happens-before B gilt, sieht B garantiert die Writes von A. Quellen sind Thread-Start/Join, Unlock eines Monitors und danach Lock, Write auf volatile und danach Read, und das Ende des Konstruktors für final-Felder (Safe Publication).

Ohne happens-before-Kante kann ein anderer Thread ein nur halb konstruiertes Objekt sehen — oder ein altes Feld für immer. Deshalb braucht Double-Checked Locking volatile auf der Instanz, und deshalb braucht das Publizieren einer HashMap vom Starter-Thread zu den Request-Threads eine Safe Publication (volatile, Lock oder eine concurrent Datenstruktur).

Nachfragen

  • Warum sind final-Felder nach dem Konstruktor besonders?
  • Ist Thread.sleep eine Memory Barrier? (keine, auf die du dich verlassen solltest)

Falle: „volatile flushed nach RAM“ als ganze Geschichte.

Merksatz: Ohne happens-before gibt es keine Visibility-Garantie.

4. Deadlock, livelock, starvation — with a Spring example?

Deutsch

Deadlock, Livelock, Starvation — mit einem Spring-Beispiel?

Level: Mid · Listen for: lock order; nested transactions / connection pools

Niveau: Mid · Darauf hören sie: Lock-Reihenfolge; nested Transactions / Connection Pools

Model answer

Deadlock: two threads each hold a lock the other needs. Classic: lock A then B vs lock B then A. In Spring, a practical deadlock is database locks (two transactions updating rows in opposite order) or pool exhaustion: thread 1 holds a JDBC connection and waits on a downstream HTTP call; all connections/threads fill up.

Livelock: threads keep yielding and retrying, making progress in the algorithm but not in the work.

Starvation: a thread never gets the lock or pool slot (unfair locks, huge tasks occupying the pool).

Fix deadlocks with a global lock order, shorter transactions, timeouts (Lock.tryLock, DB lock_timeout), and never doing remote I/O while holding a DB connection.

Follow-ups

  • How do you diagnose a Java deadlock? (jstack, thread dump, deadlocked)
  • How does REQUIRES_NEW plus a exhausted pool deadlock?

Trap: adding more threads as the first fix.

Memory sentence: Deadlock is waiting on each other; in services it is often locks plus the connection pool.

Musterantwort

Deadlock: zwei Threads halten jeweils einen Lock, den der andere braucht. Klassiker: erst A dann B vs. erst B dann A. In Spring ist ein praktischer Deadlock Database Locks (zwei Transactions updaten Rows in entgegengesetzter Reihenfolge) oder Pool Exhaustion: Thread 1 hält eine JDBC-Connection und wartet auf einen Downstream-HTTP-Call; alle Connections/Threads laufen voll.

Livelock: Threads weichen ständig aus und versuchen es neu — der Algorithmus kommt voran, die eigentliche Arbeit nicht.

Starvation: ein Thread bekommt den Lock oder Pool-Slot nie (unfair Locks, riesige Tasks, die den Pool blockieren).

Deadlocks gehst du mit einer globalen Lock-Reihenfolge an, kürzeren Transactions, Timeouts (Lock.tryLock, DB lock_timeout) — und du machst kein Remote-I/O, während du eine DB-Connection hältst.

Nachfragen

  • Wie diagnostizierst du einen Java-Deadlock? (jstack, Thread Dump, deadlocked)
  • Wie entsteht ein Deadlock bei REQUIRES_NEW plus einem erschöpften Pool?

Falle: mehr Threads als erste Maßnahme.

Merksatz: Deadlock heißt gegenseitiges Warten; in Services oft Locks plus Connection Pool.

5. AtomicInteger vs synchronized vs LongAdder?

Deutsch

AtomicInteger vs. synchronized vs. LongAdder?

Level: Mid · Listen for: CAS; contention

Niveau: Mid · Darauf hören sie: CAS; Contention

Model answer

AtomicInteger uses CAS (compare-and-set). Uncontended increments are cheaper than a monitor. Compound actions still need updateAndGet or a lock — two atomics in a row are not atomic together.

Under heavy contention, LongAdder stripes the counter across cells and is better for stats.

If you must update two fields together, you need one lock (or one atomic structure), not two Atomic* fields.

Follow-ups

  • ABA problem?
  • Why is incrementAndGet not enough for “check bound then increment”?

Trap: two atomic fields treated as one consistent snapshot.

Memory sentence: Atomics make one variable race-free; they do not compose two variables.

Musterantwort

AtomicInteger nutzt CAS (Compare-and-Set). Ohne Contention ist incrementieren günstiger als ein Monitor. Zusammengesetzte Aktionen brauchen trotzdem updateAndGet oder einen Lock — zwei Atomics hintereinander sind zusammen nicht atomar.

Unter hoher Contention verteilt LongAdder den Counter über Cells (Striping) und ist besser für Statistik-Zähler.

Wenn du zwei Felder zusammen updaten musst, brauchst du einen Lock (oder eine atomare Struktur), nicht zwei Atomic*-Felder.

Nachfragen

  • ABA-Problem?
  • Warum reicht incrementAndGet nicht für „Grenze prüfen, dann incrementieren“?

Falle: zwei Atomic-Felder als einen konsistenten Snapshot behandeln.

Merksatz: Atomics machen eine Variable race-free; zwei Variablen werden daraus nicht atomar.

6. How do you run work in a thread pool? What is wrong with new Thread()?

Deutsch

Wie führst du Arbeit in einem Thread-Pool aus? Was ist falsch an new Thread()?

Level: Mid · Listen for: ExecutorService, bounded queues, rejection, Boot’s TaskExecutor

Niveau: Mid · Darauf hören sie: ExecutorService, bounded Queues, Rejection, Boot-TaskExecutor

Model answer

new Thread() per task is unbounded, expensive, and has no backpressure. Use an ExecutorService with a bounded queue and a rejection policy.

In Spring Boot, inject a TaskExecutor / @Async executor you configured. Name the threads. Set core/max/queue from load tests. On shutdown, awaitTermination so in-flight work can finish.

If the queue is unbounded (LinkedBlockingQueue with no capacity), the pool never grows to max and you can OOM. That is the classic ThreadPoolExecutor trap.

Follow-ups

  • Cached thread pool vs fixed vs ForkJoinPool?
  • What happens to @Async if you use the default executor under load?

Trap: Executors.newCachedThreadPool() in a web app.

Memory sentence: Bound the pool and the queue; unbounded thread creation is a leak.

Musterantwort

new Thread() pro Task ist unbounded, teuer und hat kein Backpressure. Nimm einen ExecutorService mit begrenzter Queue und Rejection Policy.

In Spring Boot injizierst du einen TaskExecutor / @Async-Executor, den du konfiguriert hast. Gib den Threads Namen. Setze core/max/queue aus Lasttests. Beim Shutdown awaitTermination, damit laufende Arbeit fertig werden kann.

Ist die Queue unbounded (LinkedBlockingQueue ohne Capacity), wächst der Pool nie auf max — und du läufst in einen OOM. Das ist die klassische ThreadPoolExecutor-Falle.

Nachfragen

  • Cached Thread Pool vs. Fixed Thread Pool vs. ForkJoinPool?
  • Was passiert mit @Async, wenn du den Default-Executor unter Last nimmst?

Falle: Executors.newCachedThreadPool() in einer Web-App.

Merksatz: Pool und Queue begrenzen; unbounded Thread-Erzeugung ist ein Leak.

7. CompletableFuture — how does it relate to @Async?

Deutsch

CompletableFuture — wie hängt das mit @Async zusammen?

Level: Mid · Listen for: pipeline, which thread runs thenApply, exception handling

Niveau: Mid · Darauf hören sie: Pipeline, welcher Thread thenApply ausführt, Exception Handling

Model answer

CompletableFuture is a composable async result. thenApply runs on the same thread that completed the future (or the caller, if already complete). thenApplyAsync hops to an executor.

Always pass your executor. The common ForkJoinPool is the wrong place for blocking JDBC.

@Async on a Spring bean method that returns CompletableFuture is a proxy that submits the method body. Self-invocation still skips it. Exceptions must be handled (exceptionally, handle) or they die on a pool thread.

Follow-ups

  • join vs get vs orTimeout?
  • Why is blocking .get() inside a request thread often a deadlock risk?

Trap: blocking a Tomcat thread on .get() while the work needs that same pool.

Memory sentence: Compose futures on an explicit executor; never block a request thread on the pool it needs.

Musterantwort

CompletableFuture ist ein komponierbares asynchrones Ergebnis. thenApply läuft auf dem Thread, der das Future abgeschlossen hat (oder auf dem Aufrufer, wenn es schon fertig ist). thenApplyAsync wechselt auf einen Executor.

Immer deinen Executor übergeben. Der ForkJoinPool.commonPool() ist der falsche Ort für blocking JDBC.

@Async auf einer Spring-Bean-Methode, die CompletableFuture zurückgibt, ist ein Proxy, der den Methodenrumpf an den Executor einreicht. Self-Invocation überspringt es trotzdem. Exceptions musst du behandeln (exceptionally, handle) — sonst sterben sie auf einem Pool-Thread.

Nachfragen

  • join vs. get vs. orTimeout?
  • Warum ist blocking .get() in einem Request-Thread oft ein Deadlock-Risiko?

Falle: einen Tomcat-Thread mit .get() blockieren, während die Arbeit denselben Pool braucht.

Merksatz: Futures auf einem expliziten Executor komponieren; nie einen Request-Thread auf dem Pool blockieren, den er selbst braucht.

8. ThreadLocal — uses and leaks?

Deutsch

ThreadLocal — Nutzen und Leaks?

Level: Mid · Listen for: request context; remove() in finally; pooled threads

Niveau: Mid · Darauf hören sie: Request Context; remove() in finally; gepoolte Threads

Model answer

ThreadLocal stores a value per thread. Spring uses similar ideas for the security context (mode-dependent), transaction synchronization, and MDC logging.

Pooled threads reuse the same thread for the next request. If you set() and forget remove(), the next request sees the previous user’s data. That is both a leak and a security bug.

Always try { ... } finally { threadLocal.remove(); }. Prefer passing context as a method argument when you can.

Follow-ups

  • InheritableThreadLocal and thread pools?
  • Why do virtual threads change the cost of ThreadLocals?

Trap: storing a large object in ThreadLocal “for convenience” in a singleton.

Memory sentence: ThreadLocals on pooled threads must be cleared, or the next request inherits them.

Musterantwort

ThreadLocal speichert einen Wert pro Thread. Spring nutzt ähnliche Ideen für den Security Context (abhängig vom Mode), Transaction Synchronization und MDC-Logging.

Gepoolte Threads wiederverwenden denselben Thread für den nächsten Request. Wenn du set() machst und remove() vergisst, sieht der nächste Request die Daten des vorherigen Users. Das ist ein Leak und ein Security-Bug.

Immer try { ... } finally { threadLocal.remove(); }. Den Context lieber als Methodenargument durchreichen, wenn du kannst.

Nachfragen

  • InheritableThreadLocal und Thread-Pools?
  • Warum ändern Virtual Threads die Kosten von ThreadLocals?

Falle: ein großes Objekt „der Bequemlichkeit wegen“ in einem ThreadLocal im Singleton lagern.

Merksatz: ThreadLocals auf gepoolten Threads musst du leeren, sonst erbt der nächste Request sie.

9. Stack vs heap? Where do objects, locals, and statics live?

Deutsch

Stack vs. Heap? Wo leben Objekte, lokale Variablen und Statics?

Level: Junior · Listen for: per-thread stack; shared heap; metaspace

Niveau: Junior · Darauf hören sie: Stack pro Thread; geteilter Heap; Metaspace

Model answer

Stack: per thread; frames with local primitives and references. Heap: shared objects. Metaspace: class metadata.

A local variable User u holds a reference on the stack; the User object is on the heap. static fields are GC roots as long as the class is reachable — a common leak bucket.

Escape analysis may allocate some objects on the stack or in registers as an optimization. You still design as if objects are on the heap.

Follow-ups

  • What is a GC root?
  • Why can a huge recursion cause StackOverflowError but not OOM?

Trap: “objects declared in a method live on the stack.”

Memory sentence: References live in frames; objects live on the heap unless the JIT proves otherwise.

Musterantwort

Stack: pro Thread; Frames mit lokalen Primitives und Referenzen. Heap: geteilte Objekte. Metaspace: Class Metadata.

Eine lokale Variable User u hält eine Referenz auf dem Stack; das User-Objekt liegt auf dem Heap. static-Felder sind GC Roots, solange die Klasse erreichbar ist — ein häufiger Leak-Eimer.

Escape Analysis darf manche Objekte als Optimierung auf dem Stack oder in Registern allokieren. Du gehst trotzdem davon aus, dass Objekte auf dem Heap liegen.

Nachfragen

  • Was ist ein GC Root?
  • Warum kann eine riesige Rekursion StackOverflowError werfen, aber nicht OOM?

Falle: „Objekte, die in einer Methode deklariert sind, leben auf dem Stack.“

Merksatz: Referenzen leben in Frames; Objekte leben auf dem Heap, außer das JIT beweist etwas anderes.

10. How does GC work at a high level? What do you tune first?

Deutsch

Wie funktioniert GC grob? Womit fängst du beim Tunen an?

Level: Mid · Listen for: generations, pause vs throughput; measure first

Niveau: Mid · Darauf hören sie: Generationen, Pause vs. Throughput; erst messen

Model answer

Most HotSpot collectors are generational: new objects die young in Eden; survivors promote to old gen. Collecting young gen is frequent and cheap; full collections of a huge old gen are expensive.

G1 is the common default: it collects regions to meet a pause goal. ZGC/Shenandoah target very low pauses on large heaps.

I do not start with flags. I start with allocation rate, heap dumps, and pause metrics. The usual app-level wins: fewer short-lived objects in hot loops, bounded caches, paging, not loading 200k entities.

Follow-ups

  • What is a stop-the-world pause?
  • How do you see allocation in production?

Trap: -Xmx as the only performance tool.

Memory sentence: GC is a symptom; allocation and retained heap are the cause.

Musterantwort

Die meisten HotSpot-Collector sind generational: neue Objekte sterben jung in Eden; Survivors werden in die Old Gen promoted. Young-Gen-Collections sind häufig und billig; Full Collections einer riesigen Old Gen sind teuer.

G1 ist der übliche Default: er räumt Regions ab, um ein Pause-Ziel zu treffen. ZGC/Shenandoah zielen auf sehr niedrige Pause-Zeiten auf großen Heaps.

Ich starte nicht mit Flags. Ich starte mit Allocation Rate, Heap Dumps und Pause-Metriken. Die üblichen Hebel auf App-Ebene: weniger kurzlebige Objekte in Hot Loops, begrenzte Caches, Paging, nicht 200k Entities laden.

Nachfragen

  • Was ist eine Stop-the-World-Pause?
  • Wie siehst du Allocation in Produktion?

Falle: -Xmx als einziges Performance-Werkzeug.

Merksatz: GC ist ein Symptom; Allocation und retained Heap sind die Ursache.

11. What is safe publication of a singleton?

Deutsch

Was ist Safe Publication eines Singletons?

Level: Senior · Listen for: DCL + volatile, enum, class initialization, Spring

Niveau: Senior · Darauf hören sie: DCL + volatile, Enum, Class Initialization, Spring

Model answer

Unsafe:


if (instance == null) instance = new Foo(); // racy, may publish uninitialized

Safe options:

  • class-holder / enum singleton (JVM class init lock)
  • synchronized factory
  • double-checked locking with volatile instance
  • let Spring create the singleton bean — the container publishes it safely after initialization

In application code, prefer the container or an enum. Do not invent DCL unless you must.

Follow-ups

  • Why did DCL need volatile after Java 5?
  • Is a static final map filled in a static block safely published? (yes, class init)

Trap: lazy static field without synchronization.

Memory sentence: Spring singletons are safely published; homemade lazy statics often are not.

Musterantwort

Unsicher:

Sichere Optionen:

  • Class-Holder / Enum-Singleton (JVM Class-Init-Lock)
  • synchronized Factory
  • Double-Checked Locking mit volatile instance
  • Spring den Singleton-Bean erzeugen lassen — der Container macht ihn nach der Initialisierung sicher sichtbar

Im Anwendungscode lieber den Container oder ein Enum. DCL nicht selbst bauen, außer du musst.

Nachfragen

  • Warum brauchte DCL nach Java 5 volatile?
  • Ist eine static final Map, gefüllt in einem statischen Block, sicher sichtbar? (ja, Class Init)

Falle: lazy static-Feld ohne Synchronization.

Merksatz: Spring-Singletons sind sicher sichtbar; selbst gebaute lazy Statics oft nicht.

12. Virtual threads (Java 21) — what changes for Spring?

Deutsch

Virtual Threads (Java 21) — was ändert sich für Spring?

Level: Senior · Listen for: blocking is OK; pin; not a substitute for bounded pools of CPU work

Niveau: Senior · Darauf hören sie: Blocking ist ok; Pinning; kein Ersatz für begrenzte Pools bei CPU-Arbeit

Model answer

Virtual threads are cheap to block. They let you write thread-per-request style code without tying a platform thread to each request. Spring Boot can run Tomcat/Jetty on virtual threads.

They do not make CPU-bound work faster. Pinning (staying on a carrier while holding a synchronized monitor for a long time, historically) reduces the benefit — prefer ReentrantLock for long holds, and keep JDBC drivers up to date.

Do not create an unbounded storm of blocking tasks against a 10-connection pool. Virtual threads still need bounded access to scarce resources.

Follow-ups

  • Structured concurrency?
  • Why can virtual threads make a small connection pool fail faster?

Trap: “virtual threads mean I can ignore the Hikari pool size.”

Memory sentence: Virtual threads make blocking cheap; they do not create extra database connections.

Musterantwort

Virtual Threads sind billig zu blockieren. Du kannst Thread-per-Request-Style schreiben, ohne einen Platform Thread an jeden Request zu binden. Spring Boot kann Tomcat/Jetty auf Virtual Threads laufen lassen.

Sie machen CPU-bound Arbeit nicht schneller. Pinning (lange auf einem Carrier bleiben, während du einen synchronized-Monitor hältst — historisch) reduziert den Gewinn — wenn du lange hältst, lieber ReentrantLock, und JDBC-Treiber aktuell halten.

Keine unbegrenzte Welle blockierender Tasks gegen einen 10-Connection-Pool. Virtual Threads brauchen trotzdem begrenzten Zugriff auf knappe Ressourcen.

Nachfragen

  • Structured Concurrency?
  • Warum können Virtual Threads einen kleinen Connection Pool schneller umkippen lassen?

Falle: „Virtual Threads bedeuten, ich kann die Hikari-Pool-Größe ignorieren.“

Merksatz: Virtual Threads machen Blocking billig; sie erzeugen keine zusätzlichen Database Connections.

13. wait / notify vs higher-level concurrency utilities?

Deutsch

wait / notify vs. höhere Concurrency-Utilities?

Level: Mid · Listen for: always wait in a loop; prefer BlockingQueue

Niveau: Mid · Darauf hören sie: immer in einer Schleife warten; lieber BlockingQueue

Model answer

wait() releases the monitor and parks. notify/notifyAll wake waiters. You must wait in a loop checking the condition — spurious wakeups and missed signals are real.

I almost never write this in application code. I use BlockingQueue, CountDownLatch, Semaphore, CompletableFuture, or Spring events.

Follow-ups

  • Why notifyAll instead of notify?
  • Difference between wait and sleep? (wait releases the lock)

Trap: calling wait without holding the monitor (IllegalMonitorStateException).

Memory sentence: wait/notify are primitives; production code uses queues and latches.

Musterantwort

wait() gibt den Monitor frei und parkt. notify/notifyAll wecken wartende Threads. Du musst wait in einer Schleife aufrufen und die Condition prüfen — Spurious Wakeups und verpasste Signals sind real.

Ich schreibe das im Anwendungscode fast nie. Ich nutze BlockingQueue, CountDownLatch, Semaphore, CompletableFuture oder Spring Events.

Nachfragen

  • Warum notifyAll statt notify?
  • Unterschied zwischen wait und sleep? (wait gibt den Lock frei)

Falle: wait ohne den Monitor zu halten (IllegalMonitorStateException).

Merksatz: wait/notify sind Primitive; Produktionscode nutzt Queues und Latches.

14. How do you debug a production CPU spike or a stuck app?

Deutsch

Wie debugst du einen CPU-Spike in Produktion oder eine hängende App?

Level: Senior · Listen for: thread dump vs CPU profile vs heap dump; a method

Niveau: Senior · Darauf hören sie: Thread Dump vs. CPU-Profil vs. Heap Dump; ein Vorgehen

Model answer

I split symptoms:

  • High CPU: sampler profile (async-profiler, JFR). Look for hot methods, not guesses.
  • Stuck / no progress: thread dump (jstack or the Actuator threaddump). Look for BLOCKED, deadlock, and all threads waiting on a pool or a lock.
  • Growing memory: heap dump, then retained sizes (listeners, caches, maps).
  • Slow requests: traces + JDBC slow logs + N+1, not “add CPU”.

I do not restart first if I can still take a dump. A restart destroys the evidence.

Follow-ups

  • What does a thread dump of RUNNABLE in SocketInputStream mean?
  • How does Actuator help without SSH?

Trap: restarting as the only incident tool.

Memory sentence: CPU → profile; stuck → thread dump; memory → heap dump; slow → traces and SQL.

Musterantwort

Ich trenne nach Symptomen:

  • Hohe CPU: Sampler-Profil (async-profiler, JFR). Nach Hot Methods suchen, nicht raten.
  • Hängt / kein Fortschritt: Thread Dump (jstack oder Actuator threaddump). Nach BLOCKED, Deadlock und Threads suchen, die alle auf einem Pool oder Lock warten.
  • Wachsender Speicher: Heap Dump, dann retained Sizes (Listener, Caches, Maps).
  • Langsame Requests: Traces + JDBC Slow Logs + N+1, nicht „CPU dazugeben“.

Ich starte nicht als Erstes neu, wenn ich noch einen Dump nehmen kann. Ein Restart zerstört die Spuren.

Nachfragen

  • Was bedeutet ein Thread Dump mit RUNNABLE in SocketInputStream?
  • Wie hilft Actuator ohne SSH?

Falle: Restart als einziges Werkzeug im Incident.

Merksatz: CPU → Profil; hängt → Thread Dump; Speicher → Heap Dump; langsam → Traces und SQL.