<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AnkitDevCode</title>
    <description>The latest articles on DEV Community by AnkitDevCode (@ankitdevcode).</description>
    <link>https://dev.to/ankitdevcode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2887935%2F9bf870cc-f8c4-49fd-9073-cbf75fe0aa50.jpg</url>
      <title>DEV Community: AnkitDevCode</title>
      <link>https://dev.to/ankitdevcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ankitdevcode"/>
    <language>en</language>
    <item>
      <title>The Idempotency Pattern: Making Retries Safe in Distributed Systems</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Fri, 11 Sep 2026 12:12:56 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/the-idempotency-pattern-making-retries-safe-in-distributed-systems-hn8</link>
      <guid>https://dev.to/ankitdevcode/the-idempotency-pattern-making-retries-safe-in-distributed-systems-hn8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Networks fail. Timeouts happen. Clients retry. Message brokers may redeliver messages.In distributed systems, you often can't know whether a request actually succeeded. The server may have completed the operation, but the response could have been lost.&lt;/p&gt;

&lt;p&gt;The obvious solution is to &lt;strong&gt;retry&lt;/strong&gt;. But retries are safe only when an operation is &lt;strong&gt;idempotent&lt;/strong&gt;—running it multiple times produces the same result as running it once.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Idempotency Pattern&lt;/strong&gt; makes retries safe and helps build reliable distributed systems. This article explains the problem, common failure scenarios, how the pattern works, and how it supports safe scaling and loose coupling.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Statement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The "At-Least-Once" Reality
&lt;/h3&gt;

&lt;p&gt;Most distributed systems deliver messages or requests &lt;strong&gt;at least once&lt;/strong&gt;, not exactly once.&lt;/p&gt;

&lt;p&gt;Retries, message broker redelivery, Outbox relays, and failover mechanisms can all cause the same request to be processed more than once. This is a practical trade-off because guaranteeing exactly-once delivery across a network is extremely difficult.&lt;/p&gt;

&lt;p&gt;So the responsibility often moves to the &lt;strong&gt;receiver&lt;/strong&gt;: it must safely handle duplicate requests.&lt;/p&gt;

&lt;p&gt;If a service isn't designed for duplicates, a single retry can accidentally perform the same operation twice—with serious consequences such as duplicate payments, orders, or updates.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;sequenceDiagram
    participant Client
    participant PaymentService
    participant Database

    Client-&amp;gt;&amp;gt;PaymentService: POST /charge ($100)
    PaymentService-&amp;gt;&amp;gt;Database: charge card, deduct balance
    Database--&amp;gt;&amp;gt;PaymentService: OK
    Note over PaymentService,Client: Response lost on the way back&amp;lt;br/&amp;gt;(timeout, network blip, crash)
    Client--xClient: Times out, assumes failure
    Client-&amp;gt;&amp;gt;PaymentService: Retry: POST /charge ($100)
    PaymentService-&amp;gt;&amp;gt;Database: charge card, deduct balance AGAIN
    Database--&amp;gt;&amp;gt;PaymentService: OK
    PaymentService--&amp;gt;&amp;gt;Client: 200 OK&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The customer is charged twice for a single purchase—not because the payment failed, but because the client retried the request after not receiving the original response.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why "Just Don't Retry" Isn't an Option
&lt;/h3&gt;

&lt;p&gt;You might think: &lt;strong&gt;"If retries cause duplicates, why not avoid retries?"&lt;/strong&gt;&lt;br&gt;
Because that simply creates a different problem.&lt;/p&gt;

&lt;p&gt;Many failures are &lt;strong&gt;temporary&lt;/strong&gt;— a brief network interruption, a service restart, or a load balancer hiccup. Without retries, these temporary failures become permanent errors for the user, leading to failed requests and lost business.&lt;/p&gt;

&lt;p&gt;So retries are an important part of building &lt;strong&gt;resilient distributed systems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The real solution isn't to avoid retries. It's to make operations &lt;strong&gt;safe to repeat&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's exactly what &lt;strong&gt;idempotency&lt;/strong&gt; provides: the same request can be processed multiple times without causing unintended side effects.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A[Request sent] --&amp;gt; B{Response received?}
    B --&amp;gt;|Yes| C[Done]
    B --&amp;gt;|No| D[Did the operation execute?]
    D --&amp;gt;|Unknown| E[Retry]
    E --&amp;gt; F{Idempotent?}
    F --&amp;gt;|Yes| G[Safe to retry]
    F --&amp;gt;|No| H[Duplicate side effect]&lt;/code&gt;&lt;/pre&gt;






&lt;h2&gt;
  
  
  Where This Bites in Practice
&lt;/h2&gt;

&lt;p&gt;Idempotency becomes important anywhere a request can be &lt;strong&gt;retried, redelivered, or reprocessed&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What can go wrong without idempotency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payment processing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A timed-out payment request is retried → the customer is charged twice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Order placement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A mobile app retries after a network failure → two identical orders are created.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Message consumers (Kafka/SQS/RabbitMQ)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A broker redelivers a message → the same event is processed twice, such as deducting inventory twice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outbox relay&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The relay publishes an event but crashes before marking it as published → the event is published again after restart.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Email/notifications&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A retry sends the same notification multiple times.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distributed sagas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A saga step is retried after a coordinator failure → an action such as a refund may be applied twice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API gateways/load balancers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A gateway retries a timed-out request while the original request is still running → the operation executes twice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Batch/ETL jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A failed job is restarted from the beginning → records already processed may be inserted or updated again.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Common Problem
&lt;/h3&gt;

&lt;p&gt;All of these scenarios have the same root cause:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same operation can be executed more than once, and the system cannot tell whether it is a new request or a retry of an operation that already succeeded.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is why &lt;strong&gt;idempotency matters&lt;/strong&gt;. It allows a system to safely retry or reprocess an operation without creating unintended side effects.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Idempotency Pattern — The Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Idea
&lt;/h3&gt;

&lt;p&gt;Attach a unique &lt;strong&gt;idempotency key&lt;/strong&gt; to every request that can cause a side effect.&lt;/p&gt;

&lt;p&gt;The server stores the key along with the &lt;strong&gt;result of the operation&lt;/strong&gt;. If the same key is received again, the server knows it is a retry.&lt;/p&gt;

&lt;p&gt;Instead of executing the operation again, it &lt;strong&gt;returns the original result&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Same idempotency key = same operation → execute once, return the same result on retries.&lt;/strong&gt;&lt;/p&gt;


&lt;/blockquote&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A[Client generates&amp;lt;br/&amp;gt;idempotency key] --&amp;gt; B[Send request&amp;lt;br/&amp;gt;+ Idempotency-Key header]
    B --&amp;gt; C{Server: key seen before?}
    C --&amp;gt;|No| D[Execute operation]
    D --&amp;gt; E[Store key + result]
    E --&amp;gt; F[Return result]
    C --&amp;gt;|Yes| G[Skip execution]
    G --&amp;gt; H[Return stored result]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The key insight is that &lt;strong&gt;the client decides whether a request represents a new operation or a retry&lt;/strong&gt; because the client knows its own intent.&lt;/p&gt;

&lt;p&gt;The client generates an idempotency key &lt;strong&gt;once for each logical operation&lt;/strong&gt;— for example, when the user clicks &lt;strong&gt;"Place Order"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the request needs to be retried, the client sends the &lt;strong&gt;same key&lt;/strong&gt; again. The server uses that key to recognize the request as a retry and returns the original result instead of executing the operation again.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sequence Diagram: Idempotent Payment
&lt;/h3&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;sequenceDiagram
    participant Client
    participant PaymentService
    participant IdempotencyStore as Idempotency Store
    participant Database

    Client-&amp;gt;&amp;gt;PaymentService: POST /charge&amp;lt;br/&amp;gt;Idempotency-Key: abc-123
    PaymentService-&amp;gt;&amp;gt;IdempotencyStore: has key "abc-123"?
    IdempotencyStore--&amp;gt;&amp;gt;PaymentService: not found
    PaymentService-&amp;gt;&amp;gt;Database: BEGIN TX
    PaymentService-&amp;gt;&amp;gt;Database: charge card, deduct balance
    PaymentService-&amp;gt;&amp;gt;IdempotencyStore: store key "abc-123" + result (same TX)
    PaymentService-&amp;gt;&amp;gt;Database: COMMIT
    PaymentService--&amp;gt;&amp;gt;Client: 200 OK (charge succeeded)

    Note over Client: Response lost / times out
    Client-&amp;gt;&amp;gt;PaymentService: Retry: POST /charge&amp;lt;br/&amp;gt;Idempotency-Key: abc-123

    PaymentService-&amp;gt;&amp;gt;IdempotencyStore: has key "abc-123"?
    IdempotencyStore--&amp;gt;&amp;gt;PaymentService: found — return stored result
    PaymentService--&amp;gt;&amp;gt;Client: 200 OK (same result, no re-charge)&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Notice that the &lt;strong&gt;idempotency check, business operation, and idempotency record should be handled in the same local transaction&lt;/strong&gt;. This is important.&lt;/p&gt;

&lt;p&gt;If the idempotency check and business write happen in separate transactions, you introduce a &lt;strong&gt;dual-write problem&lt;/strong&gt;— the same kind of consistency problem that the Outbox Pattern addresses.&lt;/p&gt;

&lt;p&gt;For a database-backed implementation, the idempotency record and the business side effect should be &lt;strong&gt;committed atomically&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Either both are committed, or neither is.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  The Idempotency Store
&lt;/h3&gt;

&lt;p&gt;The idempotency store keeps track of requests that have already been processed. It can be implemented using a database table, Redis, or another durable store, depending on the system's requirements.&lt;/p&gt;

&lt;p&gt;A simple database-backed implementation might look like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;idempotency_key&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;th&gt;response_body&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;expires_at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;abc-123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;completed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"chargeId":"ch_1"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-09-10T10:00:00Z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-09-17T10:00:00Z&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Typical fields include:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;idempotency_key&lt;/code&gt;&lt;/strong&gt; — A client-generated unique key representing one logical operation. The same key is reused for all retries of that operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;status&lt;/code&gt;&lt;/strong&gt; — Tracks the processing state, such as &lt;code&gt;in_progress&lt;/code&gt; or &lt;code&gt;completed&lt;/code&gt;. Some systems may also store &lt;code&gt;failed&lt;/code&gt;, depending on how failed requests should be handled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;response_body&lt;/code&gt;&lt;/strong&gt; — Stores the result that can be returned when the same request is retried.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;created_at&lt;/code&gt;&lt;/strong&gt; — Records when the idempotency entry was created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;expires_at&lt;/code&gt;&lt;/strong&gt; — Defines how long the key should be retained. Idempotency records are usually kept for a bounded period rather than forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact fields and storage mechanism depend on the system. The important requirement is that the store can &lt;strong&gt;reliably recognize a previously processed key and return the appropriate result without repeating the side effect&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Handling the "In-Flight" Race
&lt;/h3&gt;

&lt;p&gt;What happens if a retry arrives &lt;strong&gt;while the original request is still being processed&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Without proper coordination, both requests could see that the idempotency key does not exist and execute the business operation at the same time.&lt;/p&gt;

&lt;p&gt;A common solution is to enforce a &lt;strong&gt;unique constraint on the idempotency key&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    %% Request A (Primary Winner)
    subgraph ReqA ["Request A (Primary)"]
        A1["Incoming Request"] --&amp;gt; A2["Try INSERT idempotency key"]
        A2 --&amp;gt;|Success| A3["Mark status = in_progress"]
        A3 --&amp;gt; A4["Execute Business Logic"]
        A4 --&amp;gt; A5["Update status = completed"]
        A5 --&amp;gt; A6["Persist Response"]
    end

    %% Request B (Concurrent)
    subgraph ReqB ["Request B (Concurrent)"]
        B1["Incoming Request"] --&amp;gt; B2["Try INSERT idempotency key"]
        B2 --&amp;gt;|Constraint Violation| B3["Lookup existing key in Idempotency Table"]
        B3 --&amp;gt; B4{"Status Check"}
        B4 --&amp;gt;|Completed| B5["Return Cached Response"]
        B4 --&amp;gt;|In Progress| B6["Retry with Backoff / Return 409"]
    end

    %% Race condition visualization
    A2 -.-&amp;gt;|Unique Key Conflict| B2

    %% Styling
    style A1 fill:#e0f2fe,stroke:#0369a1,stroke-width:2px,color:#0c4a6e
    style A2 fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#713f12
    style A3 fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#713f12
    style A4 fill:#f3e8ff,stroke:#7e22ce,stroke-width:2px,color:#3b0764
    style A5 fill:#dcfce7,stroke:#15803d,stroke-width:2px,color:#14532d
    style A6 fill:#dcfce7,stroke:#15803d,stroke-width:2px,color:#14532d

    style B1 fill:#e0f2fe,stroke:#0369a1,stroke-width:2px,color:#0c4a6e
    style B2 fill:#fee2e2,stroke:#b91c1c,stroke-width:2px,color:#7f1d1d
    style B3 fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#713f12
    style B4 fill:#f3e8ff,stroke:#7e22ce,stroke-width:2px,color:#3b0764
    style B5 fill:#dcfce7,stroke:#15803d,stroke-width:2px,color:#14532d
    style B6 fill:#fee2e2,stroke:#b91c1c,stroke-width:2px,color:#7f1d1d&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The first request successfully creates the idempotency record and becomes the &lt;strong&gt;owner of the operation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If another request arrives with the same key while the operation is still running, the unique constraint prevents it from creating another record. The second request must &lt;strong&gt;not execute the business operation&lt;/strong&gt;. Depending on the system, it can wait, poll for the result, or return an &lt;code&gt;in_progress&lt;/code&gt;/conflict response.&lt;/p&gt;

&lt;p&gt;Once the first request completes, the record is updated with the final status and response.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One key → one active operation → no parallel execution of the same logical request.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Different Layers Where Idempotency Applies
&lt;/h2&gt;

&lt;p&gt;Idempotency isn't a single technique — it shows up differently depending on where in the stack you apply it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. HTTP API level (client-supplied key)&lt;/strong&gt;&lt;br&gt;
Client sends an &lt;code&gt;Idempotency-Key&lt;/code&gt; header (this is literally how Stripe's and PayPal's payment APIs work). Best for client-initiated actions like charges, orders, and transfers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Message consumer level (message ID / offset)&lt;/strong&gt;&lt;br&gt;
When consuming from Kafka, SQS, or RabbitMQ, use the message's unique ID (or a business key embedded in the payload) to check "have I already processed this message?" before applying its side effects — critical because brokers guarantee at-least-once delivery by design.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    Broker((Broker)) --&amp;gt; Consumer[Consumer Service]
    Consumer --&amp;gt; Check{"Seen in&amp;lt;br/&amp;gt;processed_messages?"}

    Check --&amp;gt;|No| Process["1. Apply Side Effect&amp;lt;br/&amp;gt;2. Record message_id (Same TX)"]
    Check --&amp;gt;|Yes| Skip[Skip — Duplicate Message]

    style Broker fill:#f3e8ff,stroke:#7e22ce,stroke-width:2px,color:#3b0764
    style Consumer fill:#e0f2fe,stroke:#0369a1,stroke-width:2px,color:#0c4a6e
    style Check fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#713f12
    style Process fill:#dcfce7,stroke:#15803d,stroke-width:2px,color:#14532d
    style Skip fill:#fee2e2,stroke:#b91c1c,stroke-width:2px,color:#7f1d1d&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;3. Natural idempotency via design (idempotent-by-construction)&lt;/strong&gt;&lt;br&gt;
Some operations are inherently safe to repeat without requiring an external deduplication store, explicit state tracking, or distributed locks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Absolute vs. relative state changes:&lt;/strong&gt; An absolute update like &lt;code&gt;UPDATE accounts SET status = 'active'&lt;/code&gt; is idempotent because applying it multiple times leaves the row in the exact same state. Conversely, a relative update like &lt;code&gt;UPDATE accounts SET balance = balance - 100&lt;/code&gt; is non-idempotent because each execution subtracts more funds. &lt;em&gt;(Note: While absolute updates are mathematically idempotent, ensure they don't blindly overwrite concurrent state changes made by other transactions).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database constraints and upserts:&lt;/strong&gt; Using clauses like &lt;code&gt;INSERT ... ON CONFLICT DO NOTHING&lt;/code&gt; or &lt;code&gt;ON CONFLICT DO UPDATE&lt;/code&gt; anchored to a unique business key (such as an &lt;code&gt;order_number&lt;/code&gt;) turns duplicate write attempts into safe, predictable outcomes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RESTful HTTP semantics:&lt;/strong&gt; Designing APIs around &lt;code&gt;PUT /resource/{id}&lt;/code&gt; (which replaces the target resource entirely with the provided payload) ensures idempotency because sending the exact same payload twice yields the identical end state. In contrast, &lt;code&gt;POST /resource&lt;/code&gt; is designed for creation and is non-idempotent, spawning a new resource with a new identifier on every call.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  How This Achieves Loose Coupling
&lt;/h2&gt;

&lt;p&gt;Idempotency reduces the amount of coordination required between components in a distributed system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clients and servers can evolve independently.&lt;/strong&gt; A client can safely retry after a timeout, connection failure, or &lt;code&gt;5xx&lt;/code&gt; response without knowing whether the server already processed the request. The server guarantees that repeating the same operation won't create another side effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Producers and consumers don't need exactly-once delivery.&lt;/strong&gt; In event-driven systems, the broker can use simple &lt;strong&gt;at-least-once delivery&lt;/strong&gt; and redeliver messages when necessary. The consumer handles duplicates using its own idempotency mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failures become easier to recover from.&lt;/strong&gt; A service, relay, or workflow can retry an operation after a crash without needing to know exactly where the previous attempt stopped. Idempotency makes repeating the operation safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;At-least-once delivery + idempotent processing = reliable systems with less coordination.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each component can &lt;strong&gt;retry independently&lt;/strong&gt;, while the receiver ensures that repeated requests or messages don't produce repeated side effects.&lt;/p&gt;


&lt;h2&gt;
  
  
  How This Achieves Scaling
&lt;/h2&gt;

&lt;p&gt;Idempotency makes it easier to scale distributed systems because requests and messages can be retried or processed by different instances without repeating their side effects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safe horizontal scaling and failover.&lt;/strong&gt; A request can be retried against any healthy service instance. The idempotency key ensures that another instance doesn't perform the same operation again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simpler retry handling.&lt;/strong&gt; Clients and services can use retries with exponential backoff without worrying that a retry will automatically create another side effect. Retry limits and backoff are still important to avoid overwhelming the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At-least-once messaging becomes practical.&lt;/strong&gt; Brokers such as Kafka, SQS, and RabbitMQ can use &lt;strong&gt;at-least-once delivery&lt;/strong&gt; and redeliver messages when necessary. Idempotent consumers prevent those redeliveries from causing duplicate effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel processing with less coordination.&lt;/strong&gt; Multiple consumer instances can process messages concurrently. If a message is delivered more than once—for example, during a consumer restart or rebalance—the idempotency check prevents the side effect from being applied twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduces the impact of retries during failures.&lt;/strong&gt; Under heavy load, timeouts and retries can increase. Without idempotency, those retries may create additional work or duplicate side effects. Idempotency prevents the same operation from being applied repeatedly, helping the system recover more safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key idea is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Idempotency allows the system to scale and recover using retries and at-least-once delivery without requiring every component to coordinate exactly-once execution.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Trade-offs to Be Aware Of
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extra storage and lookup cost.&lt;/strong&gt; Idempotency requires storing and checking a key, which adds a small amount of latency, database/storage usage, and operational overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key expiration and retention.&lt;/strong&gt; Idempotency records cannot be kept forever. You need a retention period that is long enough to cover realistic retry delays while preventing unbounded storage growth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client responsibility.&lt;/strong&gt; The client must generate the key once for each logical operation and reuse the &lt;strong&gt;same key for all retries&lt;/strong&gt;. Generating a new key for every retry makes the server treat each retry as a new operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not automatic for multi-step workflows.&lt;/strong&gt; Idempotency protects an individual operation; it does not automatically make an entire workflow idempotent. In a saga or multi-step process, each side-effecting step should be designed to handle retries safely, along with the workflow's coordination and recovery logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External side effects require additional handling.&lt;/strong&gt; A local idempotency record cannot be committed atomically with an external API call. If the external system supports idempotency, the same key should be propagated downstream. Otherwise, reconciliation or another coordination mechanism may be required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency requires careful implementation.&lt;/strong&gt; A naive &lt;code&gt;check → process → save&lt;/code&gt; approach is vulnerable to race conditions when the same key arrives concurrently. Use an atomic claim, unique constraint, or another concurrency-control mechanism to ensure that only one request can own the operation.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Idempotency + Outbox: Natural Partners
&lt;/h2&gt;

&lt;p&gt;These two patterns are frequently used together because they solve two closely related reliability problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency&lt;/strong&gt; prevents the same operation from producing duplicate side effects when requests or messages are retried.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbox&lt;/strong&gt; ensures that an event generated by a successful database transaction is reliably published to a message broker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they provide a robust approach for building systems that can safely &lt;strong&gt;retry operations and reliably propagate the resulting events&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A[Business Operation] --&amp;gt; B["Outbox Pattern:&amp;lt;br/&amp;gt;Atomically write state + event locally"]
    B --&amp;gt; C["Relay Worker:&amp;lt;br/&amp;gt;Publishes event (at-least-once)"]
    C --&amp;gt; D["Idempotent Consumer:&amp;lt;br/&amp;gt;Dedupes by message_id"]
    D --&amp;gt; E["Reliable Exactly-Once Effect&amp;lt;br/&amp;gt;(without distributed transactions)"]

    style A fill:#f3e8ff,stroke:#7e22ce,stroke-width:2px,color:#3b0764
    style B fill:#e0f2fe,stroke:#0369a1,stroke-width:2px,color:#0c4a6e
    style C fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#713f12
    style D fill:#e0f2fe,stroke:#0369a1,stroke-width:2px,color:#0c4a6e
    style E fill:#dcfce7,stroke:#15803d,stroke-width:2px,color:#14532d&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Outbox guarantees an event is &lt;strong&gt;never lost&lt;/strong&gt;. Idempotency guarantees that even if the event (or the retry of a request) arrives &lt;strong&gt;more than once&lt;/strong&gt;, the end effect is as if it arrived exactly once. Together, they turn "at-least-once, unreliable network" into "reliable, correct, exactly-once-effect" system behavior — without needing expensive distributed transactions anywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without Idempotency&lt;/th&gt;
&lt;th&gt;With Idempotency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retries can cause duplicate side effects such as double charges or duplicate orders&lt;/td&gt;
&lt;td&gt;Retries with the same key can safely return the original result without repeating the side effect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avoiding retries may seem safer, but reduces resilience&lt;/td&gt;
&lt;td&gt;Retries can be used safely with appropriate limits, backoff, and jitter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Systems may try to achieve exactly-once processing across unreliable networks&lt;/td&gt;
&lt;td&gt;At-least-once delivery + idempotent processing provides reliable effective behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client and server need tighter coordination around retry outcomes&lt;/td&gt;
&lt;td&gt;Client can retry independently; the server handles duplicate requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retries during failures can amplify load and duplicate side effects&lt;/td&gt;
&lt;td&gt;Duplicate requests are absorbed, reducing the impact of retries and redelivery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key idea behind the &lt;strong&gt;Idempotency Pattern&lt;/strong&gt; is to avoid making the network guarantee exactly-once execution. Instead, the system accepts that requests or messages may be delivered more than once and makes the &lt;strong&gt;processing itself safe to repeat&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At-least-once delivery + idempotent processing = reliable retry behavior&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This simple shift makes retries, message redelivery, failover, and horizontal scaling much easier to design. Instead of trying to prevent every duplicate delivery, the system ensures that duplicates do not produce duplicate side effects.&lt;/p&gt;

</description>
      <category>idempotency</category>
      <category>distributedsystems</category>
      <category>retry</category>
      <category>outbox</category>
    </item>
    <item>
      <title>The Outbox Pattern: Solving the Dual-Write Problem in Distributed Systems</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Thu, 10 Sep 2026 16:36:42 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/the-outbox-pattern-solving-the-dual-write-problem-in-distributed-systems-392d</link>
      <guid>https://dev.to/ankitdevcode/the-outbox-pattern-solving-the-dual-write-problem-in-distributed-systems-392d</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In a microservices architecture, a single business operation often needs to update a database and notify other services through a message broker, REST API, or another integration mechanism.&lt;/p&gt;

&lt;p&gt;For example, when an order is created, the Order Service must save the order and publish an OrderCreated event.&lt;/p&gt;

&lt;p&gt;The challenge is that these are two separate operations involving two different systems. The database update may succeed while event publishing fails—or the event may be published while the database transaction rolls back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the dual-write problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Outbox Pattern solves this by storing the business data and the event in the same database transaction, and publishing the event asynchronously from the outbox.&lt;/p&gt;

&lt;p&gt;In this article, we'll see how the dual-write problem occurs, how the Outbox Pattern solves it, and how to handle retries, duplicate events, idempotency, and scalability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Throughout this article, we’ll use event publishing as the primary example for simplicity. The same underlying problem can occur whenever a database change needs to trigger an interaction with another system—for example, a REST API call, webhook, message broker, or other external operation. The examples will focus on events, but the core principles apply more broadly.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The Problem Statement
&lt;/h1&gt;

&lt;h3&gt;
  
  
  The Dual-Write Problem
&lt;/h3&gt;

&lt;p&gt;Imagine an &lt;code&gt;OrderService&lt;/code&gt; that, when an order is placed, must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Insert a row into the &lt;code&gt;orders&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;Publish an &lt;code&gt;OrderCreated&lt;/code&gt; event so that &lt;code&gt;InventoryService&lt;/code&gt;, &lt;code&gt;NotificationService&lt;/code&gt;, and &lt;code&gt;BillingService&lt;/code&gt; can react.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The naive implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;sequenceDiagram
    participant Client
    participant OrderService
    participant Database
    participant MessageBroker as Message Broker

    Client-&amp;gt;&amp;gt;OrderService: POST /orders
    OrderService-&amp;gt;&amp;gt;Database: INSERT INTO orders
    Database--&amp;gt;&amp;gt;OrderService: OK
    OrderService-&amp;gt;&amp;gt;MessageBroker: publish(OrderCreated)
    MessageBroker--&amp;gt;&amp;gt;OrderService: ACK
    OrderService--&amp;gt;&amp;gt;Client: 201 Created&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This looks fine — until you ask: &lt;strong&gt;what happens if step 2 succeeds but step 3 fails, or vice versa?&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Failure Scenarios
&lt;/h3&gt;

&lt;p&gt;The dual-write problem becomes clearer when we look at what can go wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario A — Database commit succeeds, but event publishing fails&lt;/strong&gt;&lt;br&gt;
The order is successfully saved in the database, but the broker is unavailable, the network fails, or the application crashes before publishing the event.&lt;br&gt;
The order exists, but downstream services such as InventoryService and BillingService may never know about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario B — Event publishing succeeds, but the database transaction rolls back&lt;/strong&gt;&lt;br&gt;
Downstream services may start processing an &lt;code&gt;OrderCreated&lt;/code&gt; event even though the order was never successfully committed to the Order Service's database.&lt;/p&gt;

&lt;p&gt;This can lead to inconsistent or invalid state in downstream services. &lt;br&gt;
Now you have &lt;strong&gt;phantom orders&lt;/strong&gt; in every consuming service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario C — The application crashes between the two operations&lt;/strong&gt;&lt;br&gt;
Even if you reorder operations (publish first, write DB second, or wrap both in a "try/catch and retry"), a crash between the two non-atomic operations always leaves a window where state is inconsistent. You cannot make two independent I/O calls to two independent systems (a database and a broker) atomic using application-level logic alone. There is no distributed transaction spanning both by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario D — Retries can create duplicate events&lt;/strong&gt;&lt;br&gt;
To defend against Scenario A, an engineer adds a retry: "if publish fails, try again." But now imagine the publish actually succeeded, only the ACK was lost. The retry sends a &lt;em&gt;second&lt;/em&gt; &lt;code&gt;OrderCreated&lt;/code&gt; event. Now inventory gets reserved twice.&lt;/p&gt;

&lt;p&gt;If the consumer is not idempotent, it could perform the same business operation twice—for example, attempting to reserve inventory twice.&lt;/p&gt;

&lt;p&gt;This is why reliable event-driven systems typically combine the &lt;strong&gt;Outbox Pattern&lt;/strong&gt; with &lt;strong&gt;retries and idempotent consumers&lt;/strong&gt;.&lt;/p&gt;




&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A[Business Operation]

    A --&amp;gt; B[(Database)]
    A --&amp;gt; C[Message Broker]

    B --&amp;gt;|Write| D{Atomic?}
    C --&amp;gt;|Publish| D

    D --&amp;gt;|No| E[⚠️ Dual-Write Problem]

    E --&amp;gt; F[Database succeeds&amp;lt;br/&amp;gt;Event fails]
    E --&amp;gt; G[Event succeeds&amp;lt;br/&amp;gt;Database rolls back]
    E --&amp;gt; H[Retry / crash&amp;lt;br/&amp;gt;causes duplicates]&lt;/code&gt;&lt;/pre&gt;




&lt;h1&gt;
  
  
  Where This Bites in Practice
&lt;/h1&gt;

&lt;p&gt;The dual-write problem can appear anywhere a business operation must &lt;strong&gt;persist state and publish an event&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What can go wrong without an Outbox&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E-commerce checkout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Order is saved, but &lt;code&gt;OrderCreated&lt;/code&gt; is not published → inventory may not be reserved and downstream services never learn about the order.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Payment is recorded, but the event sent to fraud detection is lost → the transaction may not be evaluated by the fraud service.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User signup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User is created, but &lt;code&gt;UserRegistered&lt;/code&gt; is not published → the email or onboarding service never receives the event.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Saga / distributed workflows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A saga step commits its local state but fails to publish the event that triggers the next step → the workflow can get stuck waiting for an event that never arrives.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CQRS / read-model synchronization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The write-side database is updated, but the event used to update the read model is lost → the read model can become stale or inconsistent.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit / compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A state change is committed, but the corresponding audit event is lost → the audit trail may contain gaps.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Despite the different use cases, the underlying problem is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A business operation changes local state and publishes an event, but those two actions cannot be committed atomically as part of the same local transaction.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the problem the &lt;strong&gt;Outbox Pattern&lt;/strong&gt; is designed to address.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Outbox Pattern — The Solution
&lt;/h1&gt;
&lt;h3&gt;
  
  
  Core Idea
&lt;/h3&gt;

&lt;p&gt;Instead of writing to the database &lt;strong&gt;and separately&lt;/strong&gt; publishing to the broker, you do only &lt;strong&gt;one&lt;/strong&gt; atomic operation: write your business data &lt;strong&gt;and&lt;/strong&gt; the event &lt;strong&gt;into the same local database, in the same ACID transaction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A dedicated background process then reads that "outbox" table and reliably relays the events to the message broker, retrying as needed, &lt;strong&gt;after&lt;/strong&gt; the transaction has safely committed.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    subgraph "Step 1: Single Atomic Transaction"
        A[OrderService] --&amp;gt;|"BEGIN TX"| B[(orders table)]
        A --&amp;gt;|"same TX"| C[(outbox table)]
        B --&amp;gt; D["COMMIT"]
        C --&amp;gt; D
    end
    D --&amp;gt; E[Message Relay / Poller]
    E --&amp;gt;|"reads unpublished rows"| C
    E --&amp;gt;|"publishes"| F[Message Broker]
    F --&amp;gt; G[Inventory Service]
    F --&amp;gt; H[Billing Service]
    F --&amp;gt; I[Notification Service]
    E --&amp;gt;|"marks row as published"| C&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Because &lt;code&gt;orders&lt;/code&gt; and &lt;code&gt;outbox&lt;/code&gt; live in the &lt;strong&gt;same database&lt;/strong&gt;, a single local ACID transaction guarantees: if the order is saved, the outbox event is saved too — and if the transaction rolls back, neither exists. There is no window where one happens without the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Outbox Table
&lt;/h3&gt;

&lt;p&gt;A typical outbox table looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;erDiagram
    OUTBOX_EVENT {
        bigint id PK
        string aggregate_type
        string aggregate_id
        string event_type
        json payload
        timestamp created_at
        timestamp published_at
    }&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The important part is that the order record and the outbox record are created in the same database transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sequence Diagram: End-to-End Flow
&lt;/h3&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;sequenceDiagram
    participant Client
    participant OrderService
    participant DB as Database (orders + outbox)
    participant Relay as Message Relay
    participant Broker as Message Broker
    participant Consumer as Downstream Service

    Client-&amp;gt;&amp;gt;OrderService: POST /orders
    OrderService-&amp;gt;&amp;gt;DB: BEGIN TX
    OrderService-&amp;gt;&amp;gt;DB: INSERT INTO orders
    OrderService-&amp;gt;&amp;gt;DB: INSERT INTO outbox (event)
    OrderService-&amp;gt;&amp;gt;DB: COMMIT
    DB--&amp;gt;&amp;gt;OrderService: OK
    OrderService--&amp;gt;&amp;gt;Client: 201 Created

    loop Poll / CDC stream
        Relay-&amp;gt;&amp;gt;DB: SELECT unpublished outbox rows
        DB--&amp;gt;&amp;gt;Relay: rows
        Relay-&amp;gt;&amp;gt;Broker: publish(event)
        Broker--&amp;gt;&amp;gt;Relay: ACK
        Relay-&amp;gt;&amp;gt;DB: mark published
    end

    Broker-&amp;gt;&amp;gt;Consumer: OrderCreated event
    Consumer-&amp;gt;&amp;gt;Consumer: reserve inventory / charge / notify&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; the client gets a response the instant the local transaction commits. Publishing to the broker happens asynchronously and reliably in the background — the client is never blocked on the broker being available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Ways to Implement the Relay
&lt;/h3&gt;

&lt;p&gt;Once the transaction commits, we need a relay to move events from the outbox table to the message broker. There are two common approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Polling Publisher&lt;/strong&gt;&lt;br&gt;
A background process periodically queries the outbox table for events that have not yet been published.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;outbox_events&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;published_at&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is simple and easy to implement, but frequent polling can add database load and introduce some publishing latency.&lt;/p&gt;

&lt;p&gt;It is often a good choice when the event volume is moderate and simplicity is more important than achieving very low latency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Change Data Capture (CDC) — e.g., Debezium&lt;/strong&gt;&lt;br&gt;
A CDC tool tails the database's transaction log (e.g., MySQL binlog, Postgres WAL) and streams outbox inserts directly to the broker in near real-time, without polling the table at all.&lt;/p&gt;

&lt;p&gt;CDC can provide lower latency and better scalability than frequent polling, particularly for high-throughput systems. However, it also introduces additional infrastructure and operational complexity.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    subgraph Polling Approach
        P1[Scheduled Job] --&amp;gt;|"SELECT ... WHERE published_at IS NULL"| P2[(Outbox Table)]
        P1 --&amp;gt; P3[Broker]
    end
    subgraph CDC Approach
        C1[Debezium / CDC Connector] --&amp;gt;|"tails WAL/binlog"| C2[(Outbox Table)]
        C1 --&amp;gt; C3[Broker]
    end&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;CDC is generally preferred in high-throughput systems: no polling overhead, lower latency, and it captures changes at the storage engine level so nothing is missed even under load.&lt;/p&gt;




&lt;h1&gt;
  
  
  How This Achieves Loose Coupling
&lt;/h1&gt;

&lt;p&gt;The Outbox Pattern isn't just a reliability trick — it's structurally what makes event-driven microservices &lt;em&gt;loosely coupled&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Producer doesn't know who's listening.&lt;/strong&gt; &lt;code&gt;OrderService&lt;/code&gt; never calls &lt;code&gt;InventoryService&lt;/code&gt; or &lt;code&gt;BillingService&lt;/code&gt; directly. It just writes an event to its own outbox. Any number of new consumers can subscribe later with zero changes to &lt;code&gt;OrderService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No synchronous dependency chain.&lt;/strong&gt; If &lt;code&gt;BillingService&lt;/code&gt; is down, &lt;code&gt;OrderService&lt;/code&gt; is completely unaffected — it already committed its transaction and moved on. Compare this to direct service-to-service calls, where a downstream outage cascades upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Producer and consumer can evolve independently.&lt;/strong&gt; As long as the event schema is respected (ideally versioned), each service can be deployed, scaled, and changed on its own timeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure isolation.&lt;/strong&gt; A bug or outage in the message broker or in a consumer doesn't threaten the producer's data integrity — the outbox table is durable, local, and transactionally consistent regardless of what's happening downstream.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    O[OrderService] --&amp;gt;|writes event, no direct call| Outbox[(Outbox)]
    Outbox --&amp;gt; Relay
    Relay --&amp;gt; Broker((Broker))
    Broker --&amp;gt; S1[Inventory Service]
    Broker --&amp;gt; S2[Billing Service]
    Broker --&amp;gt; S3[Notification Service]
    Broker --&amp;gt; S4[Analytics Service - added later, zero changes upstream]&lt;/code&gt;&lt;/pre&gt;






&lt;h1&gt;
  
  
  How This Achieves Scaling
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Producer scaling is decoupled from consumer scaling.&lt;/strong&gt; &lt;code&gt;OrderService&lt;/code&gt; throughput is governed only by its own DB, not by how fast &lt;code&gt;BillingService&lt;/code&gt; can process events. Consumers can scale horizontally (more partitions, more consumer instances) independently, at their own pace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backpressure absorption.&lt;/strong&gt; If a downstream consumer is slow or temporarily down, events simply queue up in the broker/outbox — they aren't lost, and the producer keeps accepting new requests without slowing down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal scaling of the relay itself.&lt;/strong&gt; Multiple relay instances can process the outbox table in parallel by sharding on &lt;code&gt;aggregate_id&lt;/code&gt;, with row-level locking (&lt;code&gt;SELECT ... FOR UPDATE SKIP LOCKED&lt;/code&gt;) to avoid double-publishing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDC-based relays scale near-linearly&lt;/strong&gt; with database throughput since they read the transaction log rather than executing repeated table scans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New consumers scale the system's capabilities, not its coupling.&lt;/strong&gt; Adding a 5th, 10th, or 20th downstream service to react to &lt;code&gt;OrderCreated&lt;/code&gt; costs nothing on the producer side — it's purely an additive subscription to the broker topic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Trade-offs to Be Aware Of
&lt;/h1&gt;

&lt;p&gt;No pattern is free. Be honest about these costs when adopting Outbox:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;At-least-once delivery, not exactly-once.&lt;/strong&gt; A crash between publishing and marking a row as published can cause the same event to be sent twice. &lt;strong&gt;Consumers must be idempotent&lt;/strong&gt; (e.g., dedupe using an event ID).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added latency.&lt;/strong&gt; Events aren't published in the same millisecond as the DB write — there's a small delay (milliseconds with CDC, potentially seconds with polling).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extra infrastructure.&lt;/strong&gt; You now need a relay process (or CDC pipeline like Debezium + Kafka Connect) to operate and monitor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering guarantees need care.&lt;/strong&gt; If ordering matters (e.g., events for the same order must be processed in sequence), you need to partition by &lt;code&gt;aggregate_id&lt;/code&gt; so all events for the same entity go to the same broker partition/consumer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbox table growth.&lt;/strong&gt; Published rows need to be archived or deleted periodically to avoid unbounded table growth.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without Outbox&lt;/th&gt;
&lt;th&gt;With Outbox&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Two independent, non-atomic writes (DB + broker)&lt;/td&gt;
&lt;td&gt;One atomic local transaction (DB + outbox row)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Silent event loss or duplication under failure&lt;/td&gt;
&lt;td&gt;Reliable, at-least-once delivery via relay/CDC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Producer implicitly coupled to broker availability&lt;/td&gt;
&lt;td&gt;Producer only depends on its own database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hard to add new consumers safely&lt;/td&gt;
&lt;td&gt;New consumers subscribe freely, no upstream changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling is tangled between producer and consumer&lt;/td&gt;
&lt;td&gt;Producer and consumer scale independently&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Outbox Pattern converts a &lt;strong&gt;distributed atomicity problem&lt;/strong&gt; (which is hard) into a &lt;strong&gt;local atomicity problem&lt;/strong&gt; (which databases already solve well), then hands off reliable delivery to a purpose-built relay. That's the whole trick — and it's why it underpins so much of reliable event-driven architecture today.&lt;/p&gt;

</description>
      <category>pubsub</category>
      <category>outbox</category>
      <category>distributedsystems</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Java Thread Coordination</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:09:49 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/java-thread-coordination-35dg</link>
      <guid>https://dev.to/ankitdevcode/java-thread-coordination-35dg</guid>
      <description>&lt;p&gt;Coordinating threads — making them wait for each other, hand off data, or run in a controlled order — is one of the harder parts of concurrent programming. Java's toolkit spans several layers: JVM-native primitives (&lt;code&gt;wait&lt;/code&gt;/&lt;code&gt;notify&lt;/code&gt;, &lt;code&gt;join&lt;/code&gt;), the &lt;code&gt;java.util.concurrent&lt;/code&gt; package (locks, latches, executors, atomics), and newer additions from Project Loom (virtual threads, structured concurrency). This guide covers the full set, organized bottom-up, with the key contract each API makes, why it matters, and a runnable example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Core idea:&lt;/strong&gt; thread coordination is about two things: &lt;strong&gt;controlling execution&lt;/strong&gt; and &lt;strong&gt;making memory effects visible&lt;/strong&gt;. Prefer the highest-level API that directly expresses the relationship you need.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. Basic Thread Coordination
&lt;/h2&gt;

&lt;p&gt;A useful mental model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;join()      -&amp;gt; wait for a thread
sleep()     -&amp;gt; wait for time
interrupt() -&amp;gt; request cooperative cancellation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Thread.start()&lt;/code&gt; is also a coordination boundary: actions in the calling thread before &lt;code&gt;start()&lt;/code&gt; happen-before actions in the started thread.&lt;/p&gt;

&lt;p&gt;The oldest tools, defined directly on &lt;code&gt;java.lang.Thread&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Thread.start()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;start()&lt;/code&gt; schedules a new thread to execute &lt;code&gt;run()&lt;/code&gt;. Calling &lt;code&gt;run()&lt;/code&gt; directly does &lt;strong&gt;not&lt;/strong&gt; create a new thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;

&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// concurrent execution&lt;/span&gt;
&lt;span class="c1"&gt;// worker.run(); // ordinary method call; no new thread&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Thread.join()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Blocks the calling thread until the target thread terminates.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;join()&lt;/code&gt; waits indefinitely; &lt;code&gt;join(millis)&lt;/code&gt; / &lt;code&gt;join(millis, nanos)&lt;/code&gt; time out — after which the caller resumes even if the thread is still alive, so check &lt;code&gt;isAlive()&lt;/code&gt; if that matters.&lt;/li&gt;
&lt;li&gt;Implemented internally via &lt;code&gt;wait()&lt;/code&gt;, so it responds to interruption with &lt;code&gt;InterruptedException&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Working..."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Main thread blocks here until worker finishes&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Worker done, continuing."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Thread.sleep()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Thread.sleep()&lt;/code&gt; pauses the &lt;strong&gt;current&lt;/strong&gt; thread. It is not a synchronization mechanism and does not release monitors or explicit locks held by that thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// lock is still held&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pauses the &lt;em&gt;current&lt;/em&gt; thread without releasing any locks it holds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sleeps for &lt;em&gt;at least&lt;/em&gt; the requested duration — not guaranteed to be exact.&lt;/li&gt;
&lt;li&gt;Clears the interrupt flag when it throws &lt;code&gt;InterruptedException&lt;/code&gt;; re-set it in your catch block if the thread needs to keep behaving as interrupted.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Pause for 500 ms&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Restore interrupt status&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Thread.interrupt()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Sets a thread's interrupt flag; does not forcibly stop it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If blocked in &lt;code&gt;wait&lt;/code&gt;/&lt;code&gt;sleep&lt;/code&gt;/&lt;code&gt;join&lt;/code&gt;, the target wakes immediately with &lt;code&gt;InterruptedException&lt;/code&gt; and the flag clears.&lt;/li&gt;
&lt;li&gt;If running normal code, cooperative polling (&lt;code&gt;Thread.interrupted()&lt;/code&gt; / &lt;code&gt;isInterrupted()&lt;/code&gt;) is required to notice it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isInterrupted&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Do work&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting cleanly on interrupt"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Request cooperative shutdown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Memory Visibility: &lt;code&gt;volatile&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Not a blocking mechanism, but foundational to coordination: a &lt;code&gt;volatile&lt;/code&gt; field guarantees that writes by one thread are immediately visible to reads by others, and prevents the compiler/CPU from reordering around it. Many hand-rolled "flag" coordination patterns (e.g., a &lt;code&gt;running&lt;/code&gt; flag checked in a loop) are broken without it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Runnable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Do work&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Guaranteed visible to the running thread&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;volatile&lt;/code&gt; gives visibility, &lt;strong&gt;not&lt;/strong&gt; atomicity — &lt;code&gt;volatile int count; count++;&lt;/code&gt; is still a race. For that, use &lt;code&gt;synchronized&lt;/code&gt; or the atomic classes below.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Monitor-Based Coordination (Intrinsic Locks)
&lt;/h2&gt;

&lt;p&gt;Every object carries an implicit monitor — the original coordination mechanism, defined on &lt;code&gt;Object&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;synchronized&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Acquires an object's (or a class's, for static methods) intrinsic lock on entry, releases it on exit — even via exception. Reentrant, and establishes happens-before visibility guarantees.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Object.wait()&lt;/code&gt; / &lt;code&gt;notify()&lt;/code&gt; / &lt;code&gt;notifyAll()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;wait()&lt;/code&gt; releases the monitor and suspends the thread until &lt;code&gt;notify&lt;/code&gt;/&lt;code&gt;notifyAll&lt;/code&gt; is called on the same object, or a timeout elapses. Must be called while holding the lock, or it throws &lt;code&gt;IllegalMonitorStateException&lt;/code&gt;. &lt;strong&gt;Spurious wakeups are allowed by the JLS&lt;/strong&gt;, so &lt;code&gt;wait()&lt;/code&gt; must always sit in a &lt;code&gt;while&lt;/code&gt; loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BoundedCell&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;hasValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hasValue&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wait until the current value is consumed&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;hasValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;notifyAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wake any waiting consumer&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;hasValue&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wait until a value is produced&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;hasValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;notifyAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wake any waiting producer&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;notify()&lt;/code&gt; wakes exactly one arbitrary waiter; &lt;code&gt;notifyAll()&lt;/code&gt; wakes all of them so each re-checks its own condition — generally the safer default.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;wait()&lt;/code&gt; vs &lt;code&gt;sleep()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sleep()
  -&amp;gt; pauses current thread
  -&amp;gt; does NOT release a lock

wait()
  -&amp;gt; releases the object's monitor
  -&amp;gt; waits for notification/timeout/interruption
  -&amp;gt; reacquires the monitor before returning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always guard &lt;code&gt;wait()&lt;/code&gt; with a condition loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The condition must be re-checked after every wake-up.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Explicit Lock Coordination (&lt;code&gt;java.util.concurrent.locks&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Added to overcome &lt;code&gt;synchronized&lt;/code&gt;'s rigidity: interruptible acquisition, timeouts, fairness, and multiple wait-sets per lock.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ReentrantLock&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;updateSharedState&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Critical section&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Must release manually, unlike synchronized&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Non-blocking variant&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryLock&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Got the lock&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something else instead of blocking&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When &lt;code&gt;ReentrantLock&lt;/code&gt; is preferable
&lt;/h3&gt;

&lt;p&gt;Start with &lt;code&gt;synchronized&lt;/code&gt; for simple mutual exclusion. Reach for &lt;code&gt;ReentrantLock&lt;/code&gt; when you specifically need capabilities such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tryLock()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;timed acquisition&lt;/li&gt;
&lt;li&gt;interruptible acquisition&lt;/li&gt;
&lt;li&gt;configurable fairness&lt;/li&gt;
&lt;li&gt;multiple independent &lt;code&gt;Condition&lt;/code&gt;s&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Condition&lt;/code&gt; — &lt;code&gt;await()&lt;/code&gt; / &lt;code&gt;signal()&lt;/code&gt; / &lt;code&gt;signalAll()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;ReentrantLock&lt;/code&gt; can spawn multiple &lt;code&gt;Condition&lt;/code&gt;s, letting you separate "buffer full" from "buffer empty" instead of waking every waiter indiscriminately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Condition&lt;/span&gt; &lt;span class="n"&gt;notFull&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newCondition&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Condition&lt;/span&gt; &lt;span class="n"&gt;notEmpty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newCondition&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;putIdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;takeIdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;notFull&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wait until space is available&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;putIdx&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;putIdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;putIdx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;

        &lt;span class="n"&gt;notEmpty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wake a waiting consumer&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;notEmpty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wait until an item is available&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;takeIdx&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;takeIdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;takeIdx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;--;&lt;/span&gt;

        &lt;span class="n"&gt;notFull&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wake a waiting producer&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;ReadWriteLock&lt;/code&gt; / &lt;code&gt;ReentrantReadWriteLock&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Splits a lock into a &lt;strong&gt;read lock&lt;/strong&gt; (shared, many readers concurrently) and a &lt;strong&gt;write lock&lt;/strong&gt; (exclusive) — ideal for read-heavy shared state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReentrantReadWriteLock&lt;/span&gt; &lt;span class="n"&gt;rwLock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReentrantReadWriteLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rwLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;rwLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rwLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;rwLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;StampedLock&lt;/code&gt; (Java 8+)
&lt;/h3&gt;

&lt;p&gt;Adds a third, &lt;strong&gt;optimistic read&lt;/strong&gt; mode: readers don't block at all, they just validate afterward that no write happened concurrently — much higher throughput for read-heavy, short-critical-section workloads than &lt;code&gt;ReentrantReadWriteLock&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;StampedLock&lt;/span&gt; &lt;span class="n"&gt;stampedLock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StampedLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;distanceFromOrigin&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;stamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stampedLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryOptimisticRead&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;curX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;curY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;stampedLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stamp&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// A write happened during the read&lt;/span&gt;
        &lt;span class="n"&gt;stamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stampedLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;curX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;curY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;stampedLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlockRead&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stamp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sqrt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curX&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;curX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;curY&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;curY&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;deltaX&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;deltaY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;stamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stampedLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;deltaX&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;deltaY&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stampedLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlockWrite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stamp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;StampedLock&lt;/code&gt; is &lt;strong&gt;not reentrant&lt;/strong&gt; — re-acquiring from the same thread will deadlock.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Atomic Variables &amp;amp; Compare-And-Swap (&lt;code&gt;java.util.concurrent.atomic&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Lock-free coordination for single variables, built on hardware CAS (compare-and-swap) instructions — faster than locking under contention for simple counters/flags/references.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AtomicInteger&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Atomic ++&lt;/span&gt;

&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareAndSet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Set to 10 only if the current value is 5&lt;/span&gt;

&lt;span class="nc"&gt;AtomicReference&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicReference&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"idle"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareAndSet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"idle"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"running"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Classic state-transition guard&lt;/span&gt;

&lt;span class="c1"&gt;// updateAndGet applies a function atomically,&lt;/span&gt;
&lt;span class="c1"&gt;// retrying under contention&lt;/span&gt;
&lt;span class="nc"&gt;AtomicLong&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicLong&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateAndGet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;computeDelta&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CAS mental model
&lt;/h3&gt;

&lt;p&gt;A compare-and-set operation follows this conceptual loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read current value
      ↓
calculate new value
      ↓
CAS(oldValue, newValue)
      ↓
success?
 ├── yes -&amp;gt; done
 └── no  -&amp;gt; retry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CAS is excellent for single-variable state transitions, but becomes harder to reason about when several related variables must change together.&lt;/p&gt;

&lt;p&gt;For very high-contention counters, &lt;code&gt;LongAdder&lt;/code&gt; / &lt;code&gt;DoubleAdder&lt;/code&gt; (also in &lt;code&gt;java.util.concurrent.atomic&lt;/code&gt;) outperform &lt;code&gt;AtomicLong&lt;/code&gt; by striping the counter across internal cells and summing on read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;LongAdder&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LongAdder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Efficient under heavy contention from many threads&lt;/span&gt;

&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Read current total; may reflect concurrent updates.&lt;/span&gt;
&lt;span class="c1"&gt;// Exact once updates have quiesced.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Synchronization Utilities (&lt;code&gt;java.util.concurrent&lt;/code&gt;)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;CountDownLatch&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One-shot gate: initialized with a count; &lt;code&gt;countDown()&lt;/code&gt; decrements it; &lt;code&gt;await()&lt;/code&gt; blocks until zero. &lt;strong&gt;Cannot be reset.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CountDownLatch&lt;/span&gt; &lt;span class="n"&gt;readySignal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Do setup work&lt;/span&gt;

        &lt;span class="n"&gt;readySignal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;countDown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;readySignal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Blocks until all 3 workers signal that they are ready&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All workers ready, starting run."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Latch vs Barrier vs Phaser
&lt;/h3&gt;

&lt;p&gt;A useful distinction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CountDownLatch -&amp;gt; one-shot event gate
CyclicBarrier  -&amp;gt; reusable fixed-party checkpoint
Phaser         -&amp;gt; reusable checkpoint with dynamic parties/phases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;CyclicBarrier&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Blocks N threads until all reach the barrier, releases them together, and &lt;strong&gt;automatically resets&lt;/strong&gt; for reuse across multiple phases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CyclicBarrier&lt;/span&gt; &lt;span class="n"&gt;barrier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CyclicBarrier&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"All 4 threads reached the barrier — merging results"&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Runnable&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do phase-1 work&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;barrier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle interruption or barrier failure&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Do phase-2 work only after everyone has arrived&lt;/span&gt;
&lt;span class="o"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Phaser&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Reusable barrier with a &lt;strong&gt;dynamic&lt;/strong&gt; number of parties (register/deregister at runtime) and multiple named phases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Phaser&lt;/span&gt; &lt;span class="n"&gt;phaser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Phaser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// "1" registers the main thread as a party&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;phaser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Phase 0 work&lt;/span&gt;

        &lt;span class="n"&gt;phaser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arriveAndAwaitAdvance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Wait for all parties to finish phase 0&lt;/span&gt;

        &lt;span class="c1"&gt;// Phase 1 work&lt;/span&gt;

        &lt;span class="n"&gt;phaser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arriveAndDeregister&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Complete phase 1 and remove this thread&lt;/span&gt;
    &lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;phaser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arriveAndAwaitAdvance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Main thread joins phase 0 completion&lt;/span&gt;

&lt;span class="n"&gt;phaser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arriveAndDeregister&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Main thread completes its participation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Semaphore&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Maintains a set of permits; not tied to a single owning thread, so it suits resource pools rather than pure mutual exclusion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Semaphore&lt;/span&gt; &lt;span class="n"&gt;connectionPool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Semaphore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Allows up to 5 concurrent connections&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;useConnection&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;connectionPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;acquire&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Acquire one available slot&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Use one of the 5 available connections&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;connectionPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Return the slot to the pool&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Producer/Consumer: &lt;code&gt;BlockingQueue&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Bakes coordination directly into queue operations — &lt;code&gt;put()&lt;/code&gt; blocks when full, &lt;code&gt;take()&lt;/code&gt; blocks when empty — eliminating hand-written &lt;code&gt;wait&lt;/code&gt;/&lt;code&gt;notify&lt;/code&gt; logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;BlockingQueue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayBlockingQueue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Producer&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produceItem&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Consumer&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;consume&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Design principle:&lt;/strong&gt; if a higher-level abstraction already expresses the coordination protocol, prefer it over hand-written &lt;code&gt;wait()&lt;/code&gt;/&lt;code&gt;notify()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SynchronousQueue&lt;/code&gt; is the zero-capacity special case: every &lt;code&gt;put()&lt;/code&gt; must rendezvous directly with a matching &lt;code&gt;take()&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Task Coordination
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ExecutorService&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Decouples task submission from thread management.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Blocks until all tasks complete&lt;/span&gt;

&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;awaitTermination&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MINUTES&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since Java 21, &lt;code&gt;Executors.newVirtualThreadPerTaskExecutor()&lt;/code&gt; gives this same API cheap scaling to huge numbers of blocking tasks via virtual threads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Executor lifecycle
&lt;/h3&gt;

&lt;p&gt;An &lt;code&gt;ExecutorService&lt;/code&gt; is a resource with a lifecycle. In production code, decide who owns it and who shuts it down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create
  ↓
submit tasks
  ↓
shutdown()
  ↓
awaitTermination()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;Future&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// blocks with timeout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;CompletionService&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Wraps an &lt;code&gt;ExecutorService&lt;/code&gt; and a completion queue so you can &lt;strong&gt;process results as they finish&lt;/strong&gt;, rather than in submission order — &lt;code&gt;invokeAll&lt;/code&gt; blocks until everything is done, &lt;code&gt;CompletionService&lt;/code&gt; lets you react incrementally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CompletionService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ecs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ExecutorCompletionService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Returns the Future of whichever task finishes next&lt;/span&gt;

    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Got result: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;ScheduledExecutorService&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Coordinates tasks that run after a delay or on a recurring schedule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ScheduledExecutorService&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newScheduledThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Runs once after 10 seconds"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scheduleAtFixedRate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pingHealthCheck&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;CompletableFuture&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Besides successful pipelines, learn its failure and cancellation operators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;future&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exceptionally&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fallbackValue&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;whenComplete&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;audit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;thenCompose&lt;/code&gt; when the next operation itself returns a &lt;code&gt;CompletableFuture&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;fetchUser&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenCompose&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchOrders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;thenCombine&lt;/code&gt; when two independent futures need to be combined.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;CompletableFuture&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Composable, callback-driven async pipelines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchUserId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenApplyAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchProfile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenApply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getScore&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenAccept&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Score: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Wait for several independent futures together&lt;/span&gt;
&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;future2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;future3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Blocks until all three futures complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Fork/Join Framework (&lt;code&gt;java.util.concurrent&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Coordinates recursive divide-and-conquer parallelism, using a pool of worker threads that &lt;strong&gt;steal work&lt;/strong&gt; from each other's queues (&lt;code&gt;ForkJoinPool&lt;/code&gt;) for load balancing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SumTask&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;RecursiveTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;SumTask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="nc"&gt;SumTask&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SumTask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;SumTask&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SumTask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Execute the left task asynchronously&lt;/span&gt;

        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;rightResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Compute the right task in the current thread&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rightResult&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Wait for the left task and combine both results&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ForkJoinPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commonPool&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SumTask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bigArray&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bigArray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;RecursiveAction&lt;/code&gt; is the equivalent for tasks with no return value.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Low-Level Coordination: &lt;code&gt;LockSupport&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The permit-based primitive that &lt;code&gt;ReentrantLock&lt;/code&gt; and others are built on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;signaler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do preparation work&lt;/span&gt;

    &lt;span class="nc"&gt;LockSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unpark&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Give a permit to the target thread,&lt;/span&gt;
    &lt;span class="c1"&gt;// even if the target has not parked yet&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;signaler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;LockSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;park&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Consumes the permit if one is already available;&lt;/span&gt;
&lt;span class="c1"&gt;// otherwise, blocks until unpark() is called.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike &lt;code&gt;wait&lt;/code&gt;/&lt;code&gt;notify&lt;/code&gt;, there's no race between "signal arrives first" and "wait starts first" — the permit is stored. Still subject to spurious wakeups per its javadoc, so re-check conditions after &lt;code&gt;park()&lt;/code&gt; returns.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Specialized: &lt;code&gt;Exchanger&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A rendezvous point where exactly two threads swap objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Exchanger&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Buffer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exchanger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Exchanger&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Thread A: Fills a buffer, then swaps it for an empty one&lt;/span&gt;
&lt;span class="nc"&gt;Buffer&lt;/span&gt; &lt;span class="n"&gt;filled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fillBuffer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Buffer&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nc"&gt;Buffer&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exchanger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filled&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Waits for Thread B and exchanges the filled buffer&lt;/span&gt;
&lt;span class="c1"&gt;// for the buffer supplied by Thread B&lt;/span&gt;

&lt;span class="c1"&gt;// Thread B: Takes the filled buffer and hands back an empty one&lt;/span&gt;
&lt;span class="nc"&gt;Buffer&lt;/span&gt; &lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exchanger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exchange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Buffer&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// Waits for Thread A and exchanges buffers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  12. Thread-Confinement Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ThreadLocal&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Gives each thread its own independent copy of a variable — sidesteps coordination entirely by avoiding sharing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;FORMATTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withInitial&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Date&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;FORMATTER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Each thread gets its own formatter instance,&lt;/span&gt;
    &lt;span class="c1"&gt;// so no shared mutable state or locking is required.&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;ScopedValue&lt;/code&gt; (finalized in Java 25, JEP 506)
&lt;/h3&gt;

&lt;p&gt;A safer, immutable alternative to &lt;code&gt;ThreadLocal&lt;/code&gt; for passing context (like trace IDs) into child tasks — especially virtual threads spawned by structured concurrency — without the leak/inheritance pitfalls of &lt;code&gt;InheritableThreadLocal&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;TRACE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newInstance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TRACE_ID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"req-42"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TRACE_ID.get() == "req-42" here&lt;/span&gt;
    &lt;span class="c1"&gt;// The value is available to child tasks&lt;/span&gt;
    &lt;span class="c1"&gt;// within the structured scope&lt;/span&gt;

    &lt;span class="n"&gt;processRequest&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  13. Virtual Threads
&lt;/h2&gt;

&lt;p&gt;Virtual threads make thread-per-task programming practical for large numbers of mostly blocking tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
         &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callRemoteService&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callDatabase&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They are especially useful for I/O-bound workloads. They do not provide more CPU capacity for CPU-bound work.&lt;/p&gt;

&lt;p&gt;A good rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I/O-bound, many concurrent tasks -&amp;gt; virtual threads can scale very well
CPU-bound -&amp;gt; parallelism is still limited by available CPU
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  13. Structured Concurrency: &lt;code&gt;StructuredTaskScope&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;As of late 2026, &lt;code&gt;StructuredTaskScope&lt;/code&gt; (&lt;code&gt;java.util.concurrent&lt;/code&gt;) is still a &lt;strong&gt;preview API&lt;/strong&gt; — it has re-previewed in every JDK release since 19, most recently as JEP 525 in JDK 26, with JEP 533 targeting JDK 27; it requires &lt;code&gt;--enable-preview&lt;/code&gt; and its surface may still change before finalization. It ties the lifetimes of a group of subtasks (run on virtual threads) to a single block, so cancellation and error propagation happen as one unit instead of being scattered across manually-tracked &lt;code&gt;Future&lt;/code&gt;s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nc"&gt;Subtask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;Subtask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orderTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchOrders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Wait for both subtasks to complete&lt;/span&gt;
    &lt;span class="c1"&gt;// or fail according to the scope's shutdown policy&lt;/span&gt;

    &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userTask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderTask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this is a moving target pre-finalization, check the current JEP before depending on exact method names in production code.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Supporting Concurrent Collections
&lt;/h2&gt;

&lt;p&gt;Not coordination primitives per se, but purpose-built to avoid needing external synchronization for common shared data structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ConcurrentHashMap&lt;/code&gt;&lt;/strong&gt; — thread-safe map with fine-grained internal locking/CAS; &lt;code&gt;computeIfAbsent&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt;, etc. are atomic per-key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;code&gt;CopyOnWriteArraySet&lt;/code&gt;&lt;/strong&gt; — every mutation copies the underlying array; ideal for read-heavy, rarely-mutated lists (e.g., listener lists) since reads never block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ConcurrentLinkedQueue&lt;/code&gt;&lt;/strong&gt; — lock-free unbounded FIFO queue.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;merge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;Integer:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Performs an atomic read-modify-write operation&lt;/span&gt;
&lt;span class="c1"&gt;// without requiring an external lock.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  15. &lt;code&gt;VarHandle&lt;/code&gt; (Java 9+)
&lt;/h2&gt;

&lt;p&gt;Low-level API for fine-grained atomic and volatile access to individual fields or array elements — what libraries use to implement things like custom lock-free data structures, below even &lt;code&gt;AtomicInteger&lt;/code&gt; in the stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;VarHandle&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MethodHandles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lookup&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findVarHandle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ReflectiveOperationException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExceptionInInitializerError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAndAdd&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Atomic increment without requiring an AtomicInteger field&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rarely needed in application code — mostly relevant when writing high-performance libraries.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Happens-Before: The Memory Visibility Layer
&lt;/h2&gt;

&lt;p&gt;Coordination is not only about blocking. Java also defines &lt;strong&gt;happens-before&lt;/strong&gt; relationships that determine when one thread is guaranteed to observe another thread's actions.&lt;/p&gt;

&lt;p&gt;Important examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread.start()
    actions before start()
          ↓
    actions in started thread

Thread.join()
    actions in terminated thread
          ↓
    actions after successful join()

monitor unlock
          ↓
matching monitor lock

volatile write
          ↓
subsequent read of same volatile variable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Higher-level concurrency utilities also define memory-consistency effects. This is why synchronization APIs provide more than "making a thread wait".&lt;/p&gt;

&lt;h3&gt;
  
  
  Atomicity vs visibility
&lt;/h3&gt;

&lt;p&gt;Keep these concepts separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;volatile
    -&amp;gt; visibility + ordering

AtomicInteger
    -&amp;gt; atomic operations + visibility guarantees

synchronized
    -&amp;gt; mutual exclusion + visibility + ordering
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A variable can be visible to every thread and still be updated incorrectly if the operation itself is not atomic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Using &lt;code&gt;sleep()&lt;/code&gt; as synchronization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assumeOtherThreadFinished&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is timing-based and unreliable. Prefer &lt;code&gt;join()&lt;/code&gt;, a latch, a future, or another condition mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Using &lt;code&gt;if&lt;/code&gt; instead of &lt;code&gt;while&lt;/code&gt; around &lt;code&gt;wait()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The condition must always be re-checked.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Forgetting &lt;code&gt;unlock()&lt;/code&gt; on exceptional paths
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Assuming &lt;code&gt;volatile&lt;/code&gt; makes compound operations atomic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt; &lt;span class="c1"&gt;// still a race&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Swallowing interruption
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;unless the method has a deliberate policy for handling the cancellation request.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Choosing low-level primitives too early
&lt;/h3&gt;

&lt;p&gt;Prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BlockingQueue
CountDownLatch
Semaphore
ExecutorService
CompletableFuture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when they directly express the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Tool
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Reach for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wait for a thread to finish&lt;/td&gt;
&lt;td&gt;&lt;code&gt;join()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visible flag across threads, no atomicity needed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple mutual exclusion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;synchronized&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mutual exclusion + timeouts/interruptibility/fairness&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ReentrantLock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple distinct wait conditions on one lock&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Condition&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Many readers, few writers&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ReentrantReadWriteLock&lt;/code&gt; / &lt;code&gt;StampedLock&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lock-free counters/flags/state transitions&lt;/td&gt;
&lt;td&gt;Atomic classes / &lt;code&gt;LongAdder&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-time "wait for N events" gate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CountDownLatch&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reusable "wait for all threads at a checkpoint"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CyclicBarrier&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Barrier with a dynamic, changing number of threads&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Phaser&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Limit concurrent access to a resource pool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Semaphore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Producer/consumer handoff&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BlockingQueue&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed thread pools + async task results&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ExecutorService&lt;/code&gt; / &lt;code&gt;Future&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React to results as they finish, not in order&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CompletionService&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delayed / recurring tasks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ScheduledExecutorService&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composable async pipelines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CompletableFuture&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recursive divide-and-conquer parallelism&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ForkJoinPool&lt;/code&gt; / &lt;code&gt;RecursiveTask&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Building a custom synchronizer from scratch&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LockSupport&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-to-one data swap between two threads&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Exchanger&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-thread state, no sharing at all&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ThreadLocal&lt;/code&gt; / &lt;code&gt;ScopedValue&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grouping subtasks as one cancellable unit&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;StructuredTaskScope&lt;/code&gt; (preview)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread-safe shared map/list without manual locking&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ConcurrentHashMap&lt;/code&gt; / &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>backend</category>
      <category>java</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Data Sharing between Threads</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Mon, 07 Sep 2026 11:23:38 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/data-sharing-between-threads-1kdl</link>
      <guid>https://dev.to/ankitdevcode/data-sharing-between-threads-1kdl</guid>
      <description>&lt;p&gt;In the previous article, [Java Memory Model: How Multithreading Changes the Rules], We saw that when multiple threads execute concurrently, understanding the Java code alone isn’t always enough. We also need to understand how threads interact with memory—and what guarantees the Java Memory Model provides.&lt;/p&gt;

&lt;p&gt;Now, let’s take the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do threads actually share data?
&lt;/h3&gt;

&lt;p&gt;When multiple threads are running inside the same Java application, they don't live in completely isolated worlds. They can access objects that exist in the &lt;strong&gt;shared heap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the same time, each thread has its own &lt;strong&gt;stack&lt;/strong&gt;, which contains its method calls, local variables, and execution state.&lt;/p&gt;

&lt;p&gt;This gives us a fundamental picture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fca1vru0ollz91pdvrm9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fca1vru0ollz91pdvrm9u.png" alt=" " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The one idea this whole article is built around:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each thread has its own stack. Objects on the heap can be reached — and mutated — by more than one thread at the same time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everything about Java concurrency (race conditions, visibility issues, the need for &lt;code&gt;synchronized&lt;/code&gt;, &lt;code&gt;volatile&lt;/code&gt;, atomics, and immutability) grows out of that single sentence&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The scaffolding (you already know this part)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every thread has its own &lt;strong&gt;stack&lt;/strong&gt;: each method call gets its own copy of local variables, method parameters, and references.&lt;/li&gt;
&lt;li&gt;All objects live on a &lt;strong&gt;shared heap&lt;/strong&gt;. Any thread holding a reference can read or mutate that object.&lt;/li&gt;
&lt;li&gt;An instance or static field isn’t “shared” by definition — it becomes shared the moment more than one thread can reach the object or class that owns it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Deep Dive: Stack vs. Heap Dynamics
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29uaoofk9aammoqcwpch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29uaoofk9aammoqcwpch.png" alt=" " width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Deep Dive: Local variable vs. Instance vs. Static
&lt;/h3&gt;

&lt;p&gt;Before we dive into coding, let's break down the fundamental variable types you'll use every day in Java.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffx9q78i52btq91nrft3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffx9q78i52btq91nrft3f.png" alt=" " width="800" height="718"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Deep Dive: Object vs. Reference
&lt;/h3&gt;

&lt;p&gt;• &lt;strong&gt;The Object is the house.&lt;/strong&gt; It is the actual structure built out of bricks and mortar, sitting on a plot of land. It occupies physical space in memory.&lt;br&gt;
• &lt;strong&gt;The Reference is the address.&lt;/strong&gt; It is a piece of paper with the address written on it (&lt;code&gt;123 Heap Street&lt;/code&gt;). It is not the house itself; it just tells you &lt;strong&gt;where&lt;/strong&gt; the house is so you can go find it.&lt;/p&gt;

&lt;p&gt;Java is always pass-by-value, even for objects. For primitives, the value itself is copied, so each thread gets an independent value. For objects, the reference value is copied, so each thread has its own reference pointing to the same heap object. Therefore, a mutation through one reference can be seen through the other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxs41sui6h0l9zgy8f18d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxs41sui6h0l9zgy8f18d.png" alt=" " width="799" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Technical Boundaries&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Local Variables are Thread-Safe by Design:&lt;/strong&gt; Because they are allocated inside a private stack frame, if Thread-1 and Thread-2 execute the exact same method simultaneously, they each get a completely independent copy of that &lt;strong&gt;local variable&lt;/strong&gt;(Any variable declared inside a method — including its parameters, primitives and references — lives in that method's stack frame) on their own stacks and cannot physically access by different thread's stack.**&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primitives&lt;/strong&gt; (&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;double&lt;/code&gt;, ...) — the actual value sits directly in the stack frame.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object references&lt;/strong&gt; (&lt;code&gt;Foo f&lt;/code&gt;, &lt;code&gt;String s&lt;/code&gt;, ...) — the reference (a pointer/handle) sits in the stack frame. The &lt;em&gt;object it points to&lt;/em&gt; sits separately, on the heap.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fields are Vulnerable:&lt;/strong&gt; Fields do not belong to methods; they belong to objects (Instance) or classes (Static). Therefore, they are allocated on the Heap, exposing them to concurrent modification.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  2. How Multiple Threads Can Access the Same Object
&lt;/h2&gt;

&lt;p&gt;A reference can "escape" to another thread in several common ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passed as a constructor or method argument&lt;/li&gt;
&lt;li&gt;Stored in a &lt;code&gt;static&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;Stored in a field of an object that is itself shared&lt;/li&gt;
&lt;li&gt;Placed into a shared collection (queue, map, list)&lt;/li&gt;
&lt;li&gt;Captured by a lambda or &lt;code&gt;Runnable&lt;/code&gt; passed to another thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once two threads hold a reference to the &lt;strong&gt;same object&lt;/strong&gt;, the JVM provides no automatic coordination between them. You need explicit concurrency mechanisms such as &lt;code&gt;synchronized&lt;/code&gt;, &lt;code&gt;volatile&lt;/code&gt;, locks, atomic classes, or safe publication through concurrent collections.&lt;/p&gt;

&lt;p&gt;Without proper synchronization, one thread's updates may &lt;strong&gt;not be visible&lt;/strong&gt; to the other, or their operations may &lt;strong&gt;interleave unpredictably&lt;/strong&gt;, leading to race conditions and corrupted state. This is where the &lt;strong&gt;Java Memory Model (JMM)&lt;/strong&gt; rules from the previous article become critical.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. What Shared Mutable State Really Means
&lt;/h2&gt;

&lt;p&gt;The term &lt;strong&gt;shared mutable state&lt;/strong&gt; is often used in concurrency, but the real issue comes down to &lt;strong&gt;two independent conditions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared&lt;/strong&gt; — multiple threads can access the same state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutable&lt;/strong&gt; — that state can be changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“State” itself isn't the problem; every object has state in the form of fields. The concurrency risk arises when &lt;strong&gt;the state is both shared and mutable&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Immutable&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Mutable&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Not shared&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Safe&lt;/td&gt;
&lt;td&gt;✅ Safe — thread-confined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Safe — nothing to modify&lt;/td&gt;
&lt;td&gt;⚠️ &lt;strong&gt;Requires coordination&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key takeaway is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Shared + mutable = concurrency risk.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the state is &lt;strong&gt;immutable&lt;/strong&gt;, multiple threads can safely read it. If the state is &lt;strong&gt;not shared&lt;/strong&gt;, each thread can modify its own copy without interfering with others.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;(Shared + immutable)&lt;/strong&gt; An object that is shared but immutable (e.g. a String, or a properly constructed immutable class) is generally safe to share between threads because its state cannot be changed. One important nuance: final fields alone don't automatically make a class immutable. The fields must refer to immutable objects (or otherwise safely encapsulated state), and the object must be properly constructed/publicized.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
 &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

 &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;(Mutable + not shared)&lt;/strong&gt; An object that is mutable but never shared (a local variable confined to one thread) is also safe — no other thread can see the changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;(Mutable + shared)&lt;/strong&gt; A shared mutable object is where concurrency problems can arise. If multiple threads access and modify the same state without proper coordination, their operations can race, causing lost updates, stale reads, or other inconsistent results. This is where synchronization and the Java Memory Model become important.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  4. Different Ways Threads Can Share Data
&lt;/h1&gt;

&lt;p&gt;Java provides several mechanisms for sharing or coordinating data between threads. Each approach has different trade-offs in terms of &lt;strong&gt;safety, performance, complexity, and ownership&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The important distinction is not whether data is shared, but &lt;strong&gt;how that shared data is accessed and modified&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Shared Mutable Objects — &lt;code&gt;synchronized&lt;/code&gt; / Locks
&lt;/h3&gt;

&lt;p&gt;Multiple threads can access the same object. If they modify its state, use synchronization(Intrinsic Lock- Java automatically acquires and releases the object's monitor lock.) to coordinate access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Locks such as ReentrantLock provide mutual exclusion and visibility, ensuring that changes made before unlocking become visible to a thread that subsequently acquires the same lock.&lt;/p&gt;

&lt;p&gt;Provides features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tryLock()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Interruptible lock acquisition&lt;/li&gt;
&lt;li&gt;Fairness option&lt;/li&gt;
&lt;li&gt;Multiple &lt;code&gt;Condition&lt;/code&gt;s
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the workload is &lt;strong&gt;read-heavy with fewer writes&lt;/strong&gt;, a &lt;code&gt;ReentrantReadWriteLock&lt;/code&gt; can be a good choice.&lt;/p&gt;

&lt;p&gt;It allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multiple threads to read simultaneously&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Only one thread to write at a time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Readers are blocked while a writer holds the write lock.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReentrantReadWriteLock&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReentrantReadWriteLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLock&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;these provides &lt;strong&gt;mutual exclusion and visibility&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. &lt;code&gt;volatile&lt;/code&gt; Fields
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;volatile&lt;/code&gt; when multiple threads need to see the latest value of a variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It provides &lt;strong&gt;visibility&lt;/strong&gt;, but not atomicity.&lt;/p&gt;

&lt;p&gt;So this is still unsafe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;  &lt;span class="c1"&gt;// not atomic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Atomic Classes
&lt;/h3&gt;

&lt;p&gt;Classes such as &lt;code&gt;AtomicInteger&lt;/code&gt; provide atomic operations without explicit locks.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;AtomicInteger&lt;/code&gt; when you need simple atomic operations on a shared integer, such as counters, flags, or sequence numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  One important limitation
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;AtomicInteger&lt;/code&gt; is excellent for &lt;strong&gt;simple atomic state changes&lt;/strong&gt;, but it doesn't automatically make a group of operations atomic.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&amp;gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAndGet&lt;/span&gt;&lt;span class="o"&gt;(-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The check and update are &lt;strong&gt;two separate operations&lt;/strong&gt;. Another thread could change the balance between them.&lt;/p&gt;

&lt;p&gt;For such a multi-step operation, a &lt;strong&gt;lock or a single appropriate atomic operation such as &lt;code&gt;compareAndSet()&lt;/code&gt;&lt;/strong&gt; may be required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple rule&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Locks protect a section of code; atomic classes provide atomic operations on individual pieces of shared state.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  4. Concurrent Collections
&lt;/h3&gt;

&lt;p&gt;Java provides collections designed for concurrent access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ConcurrentHashMap&lt;/span&gt;
&lt;span class="nc"&gt;CopyOnWriteArrayList&lt;/span&gt;
&lt;span class="nc"&gt;BlockingQueue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They make common collection operations safe for concurrent use.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Immutable Objects
&lt;/h3&gt;

&lt;p&gt;Immutable objects can be &lt;strong&gt;freely shared between threads&lt;/strong&gt; because their state cannot change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ankit"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the simplest and safest approaches to concurrency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Shared + Immutable = Safe to share&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  6. Message Passing
&lt;/h3&gt;

&lt;p&gt;Instead of sharing mutable state directly, threads can communicate by passing messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;BlockingQueue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedBlockingQueue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thread puts data into the queue, and another thread takes it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Transfer data instead of sharing mutable state.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  7. Thread Confinement / &lt;code&gt;ThreadLocal&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Sometimes the best approach is not to share the data at all.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ThreadLocal&lt;/code&gt; gives each thread its own value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;
&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;
&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="no"&gt;C&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each thread works with its own data, so no synchronization is needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Putting It Together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9vz7edahsjyuob0tohhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9vz7edahsjyuob0tohhj.png" alt=" " width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>threads</category>
      <category>multithreading</category>
      <category>concurency</category>
    </item>
    <item>
      <title>Java Memory Model: How Multithreading Changes the Rules</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Mon, 31 Aug 2026 16:28:22 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/java-memory-model-how-multithreading-changes-the-rules-4e9f</link>
      <guid>https://dev.to/ankitdevcode/java-memory-model-how-multithreading-changes-the-rules-4e9f</guid>
      <description>&lt;p&gt;Threads enable multiple tasks to make progress concurrently. However, concurrency raises a fundamental question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When multiple threads access the same data, how do we know what each thread will see?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Consider this simple code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks straightforward, but once multiple threads are involved, things are no longer as simple as they appear.&lt;/p&gt;

&lt;p&gt;To understand why, we need to understand the &lt;strong&gt;Java Memory Model (JMM)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is the Java Memory Model?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Java Memory Model&lt;/strong&gt; is the set of rules that governs how threads read and write &lt;strong&gt;shared memory&lt;/strong&gt; in a multi-threaded application.&lt;/p&gt;

&lt;p&gt;It provides definitive answers to core concurrency questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visibility:&lt;/strong&gt; When does a change made by one thread become visible to another?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering:&lt;/strong&gt; Can the compiler or CPU reorder operations?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;volatile&lt;/code&gt;:&lt;/strong&gt; What guarantees does the &lt;code&gt;volatile&lt;/code&gt; keyword actually provide?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;synchronized&lt;/code&gt;:&lt;/strong&gt; Why does locking a section of code flush memory updates?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Happens-before:&lt;/strong&gt; What is the underlying contract that guarantees thread safety?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent failures:&lt;/strong&gt; Why can code that looks correct in single-threaded tests break under real concurrency?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key insight
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The JMM is not a description of physical RAM. It is a &lt;strong&gt;formal specification&lt;/strong&gt; that defines the rules of &lt;strong&gt;visibility&lt;/strong&gt;, &lt;strong&gt;ordering&lt;/strong&gt;, and &lt;strong&gt;atomicity&lt;/strong&gt; across JVM threads, CPU caches, and hardware.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without the JMM, modern CPU optimizations—such as caching, store buffers, and instruction reordering—would make multi-threaded program behavior effectively unpredictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three core concepts
&lt;/h2&gt;

&lt;p&gt;When discussing the Java Memory Model, three concepts are particularly important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Atomicity&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ordering&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these explains a large part of Java concurrency.&lt;/p&gt;




&lt;h2&gt;
  
  
  1) Atomicity
&lt;/h2&gt;

&lt;p&gt;An operation is &lt;strong&gt;atomic&lt;/strong&gt; if it executes as a single, indivisible unit of work. It either happens completely, or it does not happen at all. No other thread can observe the operation in a partially completed state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;br&gt;
In Java, operations that look simple on the surface are often broken down into multiple CPU instructions. For example, &lt;code&gt;count++&lt;/code&gt; requires three steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read&lt;/li&gt;
&lt;li&gt;modify&lt;/li&gt;
&lt;li&gt;write&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If two threads execute this sequence simultaneously, their steps can interleave, causing lost updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java guarantees&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads and writes for reference variables and most primitive variables are naturally atomic.&lt;/li&gt;
&lt;li&gt;A notable exception: non-&lt;code&gt;volatile&lt;/code&gt; &lt;code&gt;long&lt;/code&gt; and &lt;code&gt;double&lt;/code&gt; reads/writes are not guaranteed to be atomic on all platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to achieve atomicity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;synchronized&lt;/code&gt; or &lt;code&gt;ReentrantLock&lt;/code&gt; to prevent concurrent entry into a critical section.&lt;/li&gt;
&lt;li&gt;Use atomic classes in &lt;code&gt;java.util.concurrent.atomic&lt;/code&gt; (e.g., &lt;code&gt;AtomicInteger&lt;/code&gt;, &lt;code&gt;AtomicLong&lt;/code&gt;), which rely on hardware-level &lt;strong&gt;compare-and-swap (CAS)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  2) Visibility
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Visibility&lt;/strong&gt; determines when a write made by one thread becomes visible to reads made by other threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;br&gt;
Modern CPUs use high-speed caches (L1/L2/L3) and store buffers to maximize execution speed. If &lt;strong&gt;Thread A&lt;/strong&gt; updates a variable, that update may remain in one core’s local cache and not be immediately flushed to main memory. If &lt;strong&gt;Thread B&lt;/strong&gt; running on another core reads the same variable, it may see stale data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common symptom&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Threads running forever in &lt;code&gt;while (!flag) {}&lt;/code&gt; loops because they never observe another thread setting &lt;code&gt;flag = true&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to achieve visibility&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mark a variable as &lt;code&gt;volatile&lt;/code&gt;: forces volatile writes to be made visible and volatile reads to observe the latest write according to the JMM.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;synchronized&lt;/code&gt; or locks: entering/exiting a critical section creates the required memory synchronization effects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where &lt;strong&gt;happens-before&lt;/strong&gt; enters the picture. A simple way to interpret it is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If action A happens-before action B, then B is guaranteed to observe the effects of A, and A is ordered before B according to the Java Memory Model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Important: &lt;code&gt;volatile&lt;/code&gt; is not atomic
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;volatile&lt;/code&gt; provides visibility and ordering guarantees, but it does &lt;strong&gt;not&lt;/strong&gt; make compound operations atomic.&lt;/p&gt;

&lt;p&gt;For example, this is still not an atomic increment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Atomicity vs. visibility (and ordering)
&lt;/h2&gt;

&lt;p&gt;Different concurrency mechanisms solve different problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;volatile&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;volatile&lt;/code&gt; primarily provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visibility&lt;/strong&gt; — a read sees the most recent &lt;code&gt;volatile&lt;/code&gt; write (as defined by the JMM).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering&lt;/strong&gt; — it establishes required ordering constraints around volatile accesses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, &lt;code&gt;volatile&lt;/code&gt; &lt;strong&gt;does not&lt;/strong&gt; make compound operations atomic (such as &lt;code&gt;count++&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Atomic classes
&lt;/h3&gt;

&lt;p&gt;Classes such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AtomicInteger&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AtomicLong&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AtomicReference&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;provide &lt;strong&gt;atomic operations&lt;/strong&gt; (for the operations they support), along with the necessary visibility and ordering guarantees.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AtomicInteger&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// atomic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: atomic classes do not automatically make an arbitrary sequence of multiple operations atomic.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;synchronized&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;synchronized&lt;/code&gt; provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mutual exclusion&lt;/strong&gt; — only one thread at a time can execute a critical section guarded by the same monitor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility&lt;/strong&gt; — changes made before releasing the monitor become visible to a thread that subsequently acquires the same monitor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering&lt;/strong&gt; — synchronization establishes happens-before relationships defined by the JMM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;count&lt;/code&gt; does not need to be &lt;code&gt;volatile&lt;/code&gt; because both reads and writes are protected by the same monitor.&lt;/p&gt;




&lt;h2&gt;
  
  
  3) Ordering
&lt;/h2&gt;

&lt;p&gt;Modern CPUs and compilers aggressively optimize code. The instructions we write are not always executed in the intuitive, line-by-line order. &lt;strong&gt;Ordering&lt;/strong&gt; guarantees ensure that execution steps occur in a predictable, non-corrupting sequence across threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;br&gt;
To maximize pipeline efficiency, the JIT compiler and CPU may reorder instructions. Reordering preserves the intended results of single-threaded code (the “as-if-serial” rule), but it can break multi-threaded logic when other threads can observe the reordering.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler or hardware may reorder these operations when it is safe from a single-thread perspective, but with multiple threads the reordering can become observable. This is one reason the JMM defines rules around &lt;strong&gt;ordering between actions performed by different threads&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Happens-before
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Happens-before&lt;/strong&gt; defines when the result of one action is guaranteed to be visible to another action.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; necessarily mean “this physically happened earlier in time.” Instead, it means there is a defined ordering relationship.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread 1:
    write data
    release lock

Thread 2:
    acquire same lock
    read data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JMM establishes a happens-before relationship between the unlock and a subsequent lock on the same monitor. Therefore, Thread 2 can reliably observe the relevant writes made before the unlock.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common happens-before rules
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Program order rule&lt;/strong&gt;&lt;br&gt;
Within a single thread, each action &lt;em&gt;happens-before&lt;/em&gt; every action that comes later in the program.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Executed by a single thread:&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Action A&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Action B (A happens-before B)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Volatile variable rule&lt;/strong&gt;&lt;br&gt;
A write to a &lt;code&gt;volatile&lt;/code&gt; field &lt;em&gt;happens-before&lt;/em&gt; every subsequent read of that same field.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VolatileExample&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Thread 1&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// 1. Plain write&lt;/span&gt;
        &lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 2. Volatile write (release)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Thread 2&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 3. Volatile read (acquire)&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 4. Guaranteed to print 42&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; writing to &lt;code&gt;ready&lt;/code&gt; happens-before reading &lt;code&gt;ready&lt;/code&gt;. By transitivity, the earlier plain write (&lt;code&gt;data = 42&lt;/code&gt;) is also guaranteed to be visible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Monitor lock rule (&lt;code&gt;synchronized&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
An unlock on a monitor (exiting a synchronized block/method) &lt;em&gt;happens-before&lt;/em&gt; every subsequent lock on the &lt;strong&gt;same&lt;/strong&gt; monitor.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SynchronizedExample&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Thread 1&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Thread 2&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Thread start rule&lt;/strong&gt;&lt;br&gt;
A call to &lt;code&gt;thread.start()&lt;/code&gt; &lt;em&gt;happens-before&lt;/em&gt; any action inside the started thread’s &lt;code&gt;run()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Guaranteed to see data = 100 because start() happens-before run()&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Thread join rule&lt;/strong&gt;&lt;br&gt;
All actions inside a thread &lt;em&gt;happen-before&lt;/em&gt; another thread successfully returns from &lt;code&gt;join()&lt;/code&gt; on that thread.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// wait for completion&lt;/span&gt;

&lt;span class="c1"&gt;// Guaranteed to see result = 500 because completion happens-before join returns&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  A Practical Rule of Thumb
&lt;/h1&gt;

&lt;p&gt;When writing concurrent Java code, ask yourself:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Is this data shared?
&lt;/h3&gt;

&lt;p&gt;If not, concurrency is usually much easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Is the shared data mutable?
&lt;/h3&gt;

&lt;p&gt;Immutable data is much easier to share safely.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Can multiple threads modify it?
&lt;/h3&gt;

&lt;p&gt;If yes, you probably need a concurrency strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Do I need atomicity?
&lt;/h3&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;
&lt;span class="nc"&gt;AtomicLong&lt;/span&gt;
&lt;span class="nc"&gt;AtomicReference&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Do I need visibility?
&lt;/h3&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;volatile&lt;/span&gt;
&lt;span class="kd"&gt;synchronized&lt;/span&gt;
&lt;span class="nc"&gt;Lock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Do multiple operations need to happen together?
&lt;/h3&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;synchronized&lt;/span&gt;
&lt;span class="nc"&gt;Lock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Can I avoid shared mutable state altogether?
&lt;/h3&gt;

&lt;p&gt;Often, that’s the best solution.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Bigger Picture
&lt;/h1&gt;

&lt;p&gt;The Java Memory Model is the foundation behind many of Java’s concurrency tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Java Memory Model
                        │
          ┌─────────────┼─────────────┐
          ↓             ↓             ↓
      Visibility     Ordering     Atomicity
          │             │             │
          └─────────────┼─────────────┘
                        ↓
                 Concurrency APIs
                        │
          ┌─────────────┼─────────────┐
          ↓             ↓             ↓
     synchronized    volatile      Atomics
          │
          ↓
       Locks
          │
          ↓
      Executors
          │
          ↓
 CompletableFuture
          │
          ↓
  Virtual Threads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding the JMM makes these APIs much easier to reason about.&lt;/p&gt;

&lt;p&gt;Instead of memorizing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use &lt;code&gt;volatile&lt;/code&gt; here."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you can ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What guarantee do I actually need?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Final Takeaway
&lt;/h1&gt;

&lt;p&gt;Threads give Java the ability to execute multiple tasks concurrently.&lt;/p&gt;

&lt;p&gt;But once multiple threads share memory, a new problem appears:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we make sure threads see and update shared data correctly?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the problem the &lt;strong&gt;Java Memory Model&lt;/strong&gt; helps define.&lt;/p&gt;

</description>
      <category>concurrency</category>
      <category>java</category>
      <category>jmm</category>
      <category>threading</category>
    </item>
    <item>
      <title>Java Concurrency: Threads</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Mon, 31 Aug 2026 15:18:08 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/threads-dg8</link>
      <guid>https://dev.to/ankitdevcode/threads-dg8</guid>
      <description>&lt;h2&gt;
  
  
  Why Do We Need Threads?
&lt;/h2&gt;

&lt;p&gt;Imagine you are running a restaurant with only one waiter.&lt;/p&gt;

&lt;p&gt;A customer places an order.&lt;/p&gt;

&lt;p&gt;The waiter takes the order to the kitchen and then &lt;strong&gt;stands there doing nothing while waiting for the food&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Only after the food is ready can the waiter serve that customer and move to the next one.&lt;/p&gt;

&lt;p&gt;That is similar to what happens when a program executes everything sequentially:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Task A → wait → finish
Task B → wait → finish
Task C → wait → finish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if Task A is waiting for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A database response&lt;/li&gt;
&lt;li&gt;A network API&lt;/li&gt;
&lt;li&gt;A file operation&lt;/li&gt;
&lt;li&gt;A message from another service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CPU may have other useful work to do while Task A is waiting.&lt;/p&gt;

&lt;p&gt;So we naturally arrive at a question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why should one task block the progress of everything else?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where &lt;strong&gt;concurrency&lt;/strong&gt; comes into the picture.&lt;/p&gt;

&lt;p&gt;Instead of making one task wait before starting another, we can have multiple tasks making progress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌── Task A → waiting for DB
                 │
Application ─────┼── Task B → running
                 │
                 └── Task C → running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is one of the fundamental reasons we have &lt;strong&gt;threads&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Is a Thread?
&lt;/h1&gt;

&lt;p&gt;A &lt;strong&gt;thread&lt;/strong&gt; is an independent path of execution inside a process.&lt;/p&gt;

&lt;p&gt;A Java application can have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Java Process
│
├── Thread 1
├── Thread 2
├── Thread 3
└── Thread 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each thread can execute work independently while sharing resources such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heap memory&lt;/li&gt;
&lt;li&gt;Objects&lt;/li&gt;
&lt;li&gt;Static variables&lt;/li&gt;
&lt;li&gt;Other application resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running in another thread"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the JVM can execute this work concurrently with other work.&lt;/p&gt;




&lt;h1&gt;
  
  
  The First Big Win: Concurrency
&lt;/h1&gt;

&lt;p&gt;Threads can improve application responsiveness and throughput, especially when tasks spend significant time waiting for I/O.&lt;/p&gt;

&lt;p&gt;But there is a catch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Threads solved one problem and introduced a whole new class of problems.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The Dark Side of Threads (Dark Side of Shared Memory)
&lt;/h1&gt;

&lt;p&gt;Once multiple threads start executing at the same time and sharing memory, our program becomes much harder to reason about.&lt;/p&gt;

&lt;p&gt;With a single thread, execution is relatively predictable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A → B → C → D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With multiple threads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread 1: A → B →      → D
Thread 2:      X → Y → Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact order can change from one execution to another.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;concurrency bugs&lt;/strong&gt; begin.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Race Conditions
&lt;/h1&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like one operation.&lt;/p&gt;

&lt;p&gt;But conceptually it involves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read count
    ↓
add 1
    ↓
write count
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine two threads execute it at the same time.&lt;/p&gt;

&lt;p&gt;Suppose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both threads may read &lt;code&gt;5&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread 1 → reads 5
Thread 2 → reads 5

Thread 1 → writes 6
Thread 2 → writes 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One update has been lost.&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;race condition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The result depends on the timing and interleaving of threads.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Visibility Problems
&lt;/h1&gt;

&lt;p&gt;Another problem is &lt;strong&gt;visibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Modern CPUs use multiple levels of caches to improve performance.&lt;/p&gt;

&lt;p&gt;A simplified view looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          Main Memory
               │
       ┌───────┴───────┐
       ↓               ↓
   CPU Core 1       CPU Core 2
       │               │
     Cache           Cache
       │               │
   Thread A         Thread B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one thread updates shared data, another thread needs the appropriate &lt;strong&gt;Java Memory Model guarantees&lt;/strong&gt; to reliably observe that update.&lt;/p&gt;

&lt;p&gt;This is why Java provides mechanisms such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;volatile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;synchronized&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Lock&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Atomic classes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;volatile&lt;/code&gt; provides visibility and ordering guarantees for that variable.&lt;/p&gt;

&lt;p&gt;But it does &lt;strong&gt;not&lt;/strong&gt; make every compound operation atomic.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is still not made thread-safe merely by declaring &lt;code&gt;count&lt;/code&gt; as &lt;code&gt;volatile&lt;/code&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Deadlocks
&lt;/h1&gt;

&lt;p&gt;Now introduce locks.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread 1
    │
    ├── holds Lock A
    │
    └── waits for Lock B

Thread 2
    │
    ├── holds Lock B
    │
    └── waits for Lock A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither thread can continue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread 1 → waiting for Thread 2
Thread 2 → waiting for Thread 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Forever.&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;deadlock&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The application may appear completely frozen even though the process itself is still alive.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Livelock
&lt;/h1&gt;

&lt;p&gt;A livelock is different.&lt;/p&gt;

&lt;p&gt;The threads are not blocked.&lt;/p&gt;

&lt;p&gt;They are actively doing something—but they aren't making progress.&lt;/p&gt;

&lt;p&gt;Think about two people walking toward each other in a narrow hallway.&lt;/p&gt;

&lt;p&gt;One moves left.&lt;/p&gt;

&lt;p&gt;The other also moves left.&lt;/p&gt;

&lt;p&gt;Then both move right.&lt;/p&gt;

&lt;p&gt;Then both move right again.&lt;/p&gt;

&lt;p&gt;They are active, but nobody gets anywhere.&lt;/p&gt;

&lt;p&gt;That's roughly what a livelock looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread A → changes state
Thread B → reacts
Thread A → reacts
Thread B → reacts
       ↓
No actual progress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  5. Starvation
&lt;/h1&gt;

&lt;p&gt;Another problem is &lt;strong&gt;starvation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A thread may continuously fail to get the CPU time or lock access it needs because other threads keep getting priority.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread A → continuously gets access
Thread B → waits
Thread C → waits
Thread D → waits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thread B may technically be runnable but rarely gets a chance to make progress.&lt;/p&gt;




&lt;h1&gt;
  
  
  Threads Also Have a Cost
&lt;/h1&gt;

&lt;p&gt;Concurrency sounds great.&lt;/p&gt;

&lt;p&gt;So why not simply create thousands or millions of threads?&lt;/p&gt;

&lt;p&gt;Because traditional &lt;strong&gt;platform threads are relatively expensive resources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A platform thread is associated with an operating-system thread, and each thread requires memory and scheduling resources.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is generally not a good design.&lt;/p&gt;

&lt;p&gt;The exact memory cost varies by JVM, operating system, architecture, and configuration, but thousands of platform threads can consume substantial memory.&lt;/p&gt;




&lt;h1&gt;
  
  
  Context Switching
&lt;/h1&gt;

&lt;p&gt;There is another cost.&lt;/p&gt;

&lt;p&gt;Suppose the CPU is executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operating system may need to switch to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system has to preserve and restore execution state.&lt;/p&gt;

&lt;p&gt;Simplified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread A running
      ↓
Save A's state
      ↓
Load B's state
      ↓
Thread B running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;context switching&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Context switching is necessary, but excessive switching adds overhead.&lt;/p&gt;

&lt;p&gt;The CPU can end up spending more time managing execution than doing useful application work.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Real Problem
&lt;/h1&gt;

&lt;p&gt;We now have an interesting dilemma.&lt;/p&gt;

&lt;h3&gt;
  
  
  One thread
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Simple
Safe
Easy to understand

       BUT

Poor concurrency
Waiting blocks progress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Many platform threads
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;High concurrency
Better utilization

       BUT

More memory
More scheduling overhead
Race conditions
Deadlocks
Visibility problems
Harder debugging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we arrive at:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How can we get the benefits of concurrency without drowning in its complexity and resource cost?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this question drives much of Java's concurrency evolution.&lt;/p&gt;




&lt;h1&gt;
  
  
  Java's Concurrency Evolution
&lt;/h1&gt;

&lt;p&gt;Java didn't solve everything with one feature.&lt;/p&gt;

&lt;p&gt;Instead, concurrency evolved step by step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Threads
   ↓
Synchronization
   ↓
java.util.concurrent
   ↓
ExecutorService
   ↓
Thread Pools
   ↓
Future
   ↓
CompletableFuture
   ↓
Reactive Programming
   ↓
Virtual Threads
   ↓
Structured Concurrency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step addressed problems introduced by the previous approach.&lt;/p&gt;




&lt;h1&gt;
  
  
  Java 1.0 — Threads
&lt;/h1&gt;

&lt;p&gt;The fundamental building block was the &lt;code&gt;Thread&lt;/code&gt; API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;doWork&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gave developers the ability to execute work concurrently.&lt;/p&gt;

&lt;p&gt;But manually creating and managing threads doesn't scale very well.&lt;/p&gt;

&lt;p&gt;That led to the next question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Instead of creating threads ourselves, can Java manage them for us?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Java 5 — Executors and &lt;code&gt;java.util.concurrent&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Java 5 introduced a major concurrency upgrade through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;concurrent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the most important additions was &lt;code&gt;ExecutorService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a thread."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;we could think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Submit a task."&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the application could reuse a limited number of threads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             Tasks
        ┌──────┼──────┐
        ↓      ↓      ↓
      Task   Task   Task
        │      │      │
        └──────┼──────┘
               ↓
         Thread Pool
        ┌────┬────┬────┐
        │ T1 │ T2 │ T3 │
        └────┴────┴────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduced the need to constantly create new platform threads.&lt;/p&gt;




&lt;h1&gt;
  
  
  Thread Pools: Better, But Not Perfect
&lt;/h1&gt;

&lt;p&gt;Thread pools solved the thread-creation problem.&lt;/p&gt;

&lt;p&gt;But they introduced a new limitation.&lt;/p&gt;

&lt;p&gt;Suppose we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 platform threads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and all 100 are waiting for database or network responses.&lt;/p&gt;

&lt;p&gt;Now we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 threads
      ↓
100 blocked operations
      ↓
No thread available for new work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can increase the pool size.&lt;/p&gt;

&lt;p&gt;Maybe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 → 500 → 1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But eventually we hit resource limits.&lt;/p&gt;

&lt;p&gt;This is particularly important for modern applications where thousands of requests may spend most of their time waiting for I/O.&lt;/p&gt;




&lt;h1&gt;
  
  
  Java 8 — CompletableFuture
&lt;/h1&gt;

&lt;p&gt;Java 8 introduced &lt;code&gt;CompletableFuture&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It provided a way to compose asynchronous operations.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenApply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getOrders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenApply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;calculateTotal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Independent operations could also be executed concurrently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;getUserAsync&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;getOrdersAsync&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helped applications avoid some unnecessary blocking and compose asynchronous workflows.&lt;/p&gt;

&lt;p&gt;But there was a trade-off.&lt;/p&gt;

&lt;p&gt;As asynchronous workflows became more complicated, the code could become harder to read and reason about.&lt;/p&gt;




&lt;h1&gt;
  
  
  Reactive Programming
&lt;/h1&gt;

&lt;p&gt;The next major approach was &lt;strong&gt;reactive programming&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The basic idea was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't keep a thread blocked while waiting for I/O.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of returning a value directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you might return something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mono&amp;lt;User&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flux&amp;lt;Order&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allowed applications to handle large numbers of concurrent I/O operations using non-blocking execution.&lt;/p&gt;

&lt;p&gt;Reactive programming can be extremely powerful.&lt;/p&gt;

&lt;p&gt;But it introduces a different programming model and concepts such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publishers&lt;/li&gt;
&lt;li&gt;Subscribers&lt;/li&gt;
&lt;li&gt;Operators&lt;/li&gt;
&lt;li&gt;Schedulers&lt;/li&gt;
&lt;li&gt;Backpressure&lt;/li&gt;
&lt;li&gt;Reactive pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For some applications, this complexity is worthwhile.&lt;/p&gt;

&lt;p&gt;For others, developers wanted something simpler.&lt;/p&gt;

&lt;p&gt;And this is where &lt;strong&gt;Project Loom&lt;/strong&gt; changed the conversation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Project Loom
&lt;/h1&gt;

&lt;p&gt;Project Loom asked a fascinating question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can we make threads cheap enough that developers can use a simple synchronous programming model even when handling huge numbers of concurrent tasks?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual Threads.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Java 21 — Virtual Threads
&lt;/h1&gt;

&lt;p&gt;Virtual Threads became a standard feature in Java 21.&lt;/p&gt;

&lt;p&gt;Instead of requiring one heavyweight platform thread for every concurrent task, the JVM can manage a very large number of lightweight virtual threads over a smaller number of platform threads.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          Thousands of Tasks
                  ↓
          Virtual Threads
                  ↓
       ┌──────────┼──────────┐
       ↓          ↓          ↓
   Carrier 1   Carrier 2   Carrier 3
       │          │          │
       └──────────┼──────────┘
                  ↓
                 CPU
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating a virtual thread is much cheaper than creating a platform thread.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;callDatabase&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
         &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callService&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Why Are Virtual Threads Important?
&lt;/h1&gt;

&lt;p&gt;The interesting part isn't simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Virtual threads are faster."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not the right way to think about them.&lt;/p&gt;

&lt;p&gt;The real idea is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Virtual threads make concurrency cheaper.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With platform threads, we often had to think carefully about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How big should my thread pool be?
How many concurrent requests can I handle?
Will these threads consume too much memory?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Virtual threads change the economics of that decision.&lt;/p&gt;

&lt;p&gt;You can often model application work more naturally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One task
   ↓
One virtual thread
   ↓
Perform blocking-style I/O
   ↓
Virtual thread can be suspended
   ↓
Platform thread can execute other work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JVM manages the underlying scheduling.&lt;/p&gt;




&lt;h1&gt;
  
  
  From "How Many Threads?" to "How Many Tasks?"
&lt;/h1&gt;

&lt;p&gt;This is perhaps the biggest conceptual change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Limited platform threads
        ↓
Thread pool
        ↓
Tasks wait in queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Virtual-thread approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Many concurrent tasks
        ↓
Virtual threads
        ↓
JVM manages execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer can focus more on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What work needs to happen?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;rather than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do I manually manage a scarce thread resource?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  But Virtual Threads Don't Remove Concurrency Problems
&lt;/h1&gt;

&lt;p&gt;This is extremely important.&lt;/p&gt;

&lt;p&gt;Virtual threads do &lt;strong&gt;not&lt;/strong&gt; magically eliminate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Race conditions&lt;/li&gt;
&lt;li&gt;Deadlocks&lt;/li&gt;
&lt;li&gt;Incorrect synchronization&lt;/li&gt;
&lt;li&gt;Shared mutable state&lt;/li&gt;
&lt;li&gt;Poor database design&lt;/li&gt;
&lt;li&gt;External service bottlenecks&lt;/li&gt;
&lt;li&gt;CPU limitations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;from multiple concurrent threads, it can still be unsafe.&lt;/p&gt;

&lt;p&gt;Virtual threads make threads cheaper.&lt;/p&gt;

&lt;p&gt;They don't make shared mutable state safe.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Next Step: Structured Concurrency
&lt;/h1&gt;

&lt;p&gt;Once concurrency becomes cheap, another question appears:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we manage thousands of concurrent tasks safely?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
│
├── User Service
├── Order Service
└── Payment Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tasks belong to the same request.&lt;/p&gt;

&lt;p&gt;If the request is cancelled, it often makes sense for its child tasks to be cancelled too.&lt;/p&gt;

&lt;p&gt;This is the idea behind &lt;strong&gt;Structured Concurrency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parent Task
│
├── Child Task A
├── Child Task B
└── Child Task C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lifetime of the child tasks is tied to the parent.&lt;/p&gt;

&lt;p&gt;This makes concurrent code easier to reason about and manage.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Bigger Picture
&lt;/h1&gt;

&lt;p&gt;Java concurrency isn't a random collection of APIs.&lt;/p&gt;

&lt;p&gt;It is an evolution.&lt;/p&gt;

&lt;p&gt;Each generation tried to solve a problem created by the previous generation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 WHY?

Sequential execution
        ↓
"Why wait for everything?"
        ↓
Threads
        ↓
"Threads are difficult to manage."
        ↓
Executors / Thread Pools
        ↓
"Threads are still expensive when blocked."
        ↓
Async / CompletableFuture
        ↓
"Async code is becoming difficult to reason about."
        ↓
Reactive Programming
        ↓
"Can we get scalability with simpler code?"
        ↓
Virtual Threads
        ↓
"How do we structure thousands of concurrent tasks?"
        ↓
Structured Concurrency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  The Core Lesson
&lt;/h1&gt;

&lt;p&gt;The history of Java concurrency can be understood through one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How can we make more progress at the same time without making our applications too expensive or too complicated?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Threads gave us concurrency.&lt;/p&gt;

&lt;p&gt;Executors gave us thread management.&lt;/p&gt;

&lt;p&gt;Thread pools gave us controlled resource usage.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CompletableFuture&lt;/code&gt; gave us composable asynchronous workflows.&lt;/p&gt;

&lt;p&gt;Reactive programming gave us highly scalable non-blocking pipelines.&lt;/p&gt;

&lt;p&gt;Virtual threads made large-scale concurrency much cheaper while preserving a familiar programming model.&lt;/p&gt;

&lt;p&gt;Structured concurrency aims to make that concurrency easier to manage.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Mental Model
&lt;/h1&gt;

&lt;p&gt;Think of the evolution like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Threads
  │
  │  "Run things concurrently"
  ↓
Executors
  │
  │  "Manage threads"
  ↓
Thread Pools
  │
  │  "Reuse limited resources"
  ↓
CompletableFuture
  │
  │  "Compose async work"
  ↓
Reactive
  │
  │  "Handle massive non-blocking I/O"
  ↓
Virtual Threads
  │
  │  "Make concurrency lightweight"
  ↓
Structured Concurrency
     "Make concurrency manageable"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is the story of Java concurrency:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;We started by creating threads.Then we learned how difficult threads could be.So we built abstractions around them.Then we made concurrency asynchronous.Then reactive.And eventually, Java made threads cheap again.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;From &lt;code&gt;Thread&lt;/code&gt; to Virtual Threads — Java's concurrency story is really the story of making concurrent programming easier, cheaper, and more scalable.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>concurrency</category>
      <category>thread</category>
      <category>multithreading</category>
    </item>
    <item>
      <title>JPA Mapping with Hibernate- Many-to-Many Relationship</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 14 Mar 2026 11:56:20 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-many-to-many-relationship-3p82</link>
      <guid>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-many-to-many-relationship-3p82</guid>
      <description>&lt;p&gt;In the previous section, we discussed the &lt;a href="https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-many-and-many-to-one-relationship-38nf"&gt;One-to-Many and Many-to-One Relationship&lt;/a&gt; Now, let’s look at the Many-to-many relationship&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;The Relational Model Behind @ManyToMany&lt;/li&gt;
&lt;li&gt;Unidirectional @ManyToMany — Simplest Form&lt;/li&gt;
&lt;li&gt;Bidirectional @ManyToMany&lt;/li&gt;
&lt;li&gt;equals() and hashCode() — The Critical Foundation&lt;/li&gt;
&lt;li&gt;Intermediate Entity for Join Table With Extra Columns&lt;/li&gt;
&lt;li&gt;Fetch Strategies &amp;amp; The N+1 Problem&lt;/li&gt;
&lt;li&gt;Cascade Types — What to Use and When&lt;/li&gt;
&lt;li&gt;Serialization — Avoiding Infinite Recursion&lt;/li&gt;
&lt;li&gt;Performance Best Practices&lt;/li&gt;
&lt;li&gt;Quick Reference — Best Practices vs Pitfalls&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;A many-to-many (M:N) relationship is one of the most common yet most misunderstood associations in relational modeling. When mapped carelessly in JPA/Hibernate, it becomes a prime source of N+1 query problems, infinite JSON recursion, unnecessary eager loading, and subtle data-integrity bugs.&lt;/p&gt;

&lt;p&gt;This article walks through every important aspect of &lt;code&gt;@ManyToMany&lt;/code&gt;, from the simplest unidirectional form to a full intermediate-entity approach, and pairs every concept with the best practices that keep your application performant and maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Relational Model Behind @ManyToMany
&lt;/h2&gt;

&lt;p&gt;In a relational database, an M:N relationship is always implemented via a &lt;strong&gt;join table&lt;/strong&gt; (also called a bridge or association table). For example, a &lt;code&gt;Student ↔ Course&lt;/code&gt; relationship requires a &lt;code&gt;student_course&lt;/code&gt; join table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;students           student_course         courses
──────────────     ─────────────────      ──────────────────
student_id (PK)    student_id  (FK) ─▶    course_id (PK)
name               course_id   (FK) ─▶    title
email              enrolled_at            credits
                   grade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If the join table carries extra columns (&lt;code&gt;enrolled_at&lt;/code&gt;, &lt;code&gt;grade&lt;/code&gt;), you &lt;strong&gt;must&lt;/strong&gt; model it as a separate entity — a plain &lt;code&gt;@JoinTable&lt;/code&gt; annotation cannot capture those columns.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Unidirectional @ManyToMany — Simplest Form
&lt;/h2&gt;

&lt;p&gt;Use this when only one side needs to navigate to the other, and the join table has &lt;strong&gt;no extra columns&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Mapping
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinTable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student_course"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;joinColumns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student_id"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;inverseJoinColumns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"course_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// ← Set, never List&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// No back-reference here → unidirectional&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Best Practice — Use &lt;code&gt;Set&lt;/code&gt;, not &lt;code&gt;List&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Always use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; for &lt;code&gt;@ManyToMany&lt;/code&gt; collections. Hibernate's handling of &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; in many-to-many associations can throw &lt;code&gt;MultipleBagFetchException&lt;/code&gt; when fetching multiple bag collections in the same query, and may produce duplicate records.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Bidirectional @ManyToMany
&lt;/h2&gt;

&lt;p&gt;Bidirectional mapping lets both sides navigate to each other. Exactly &lt;strong&gt;one side&lt;/strong&gt; must be the owning side (holds &lt;code&gt;@JoinTable&lt;/code&gt;); the other is the inverse side (uses &lt;code&gt;mappedBy&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Mapping
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;                            &lt;span class="c1"&gt;// OWNING SIDE&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinTable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student_course"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;joinColumns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student_id"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;inverseJoinColumns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"course_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;                            &lt;span class="c1"&gt;// INVERSE SIDE&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"courses"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;            &lt;span class="c1"&gt;// mappedBy is mandatory&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — Forgetting &lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;mappedBy&lt;/code&gt; on the inverse side, JPA creates &lt;strong&gt;two independent join tables&lt;/strong&gt; and double-inserts every link row. Always declare &lt;code&gt;mappedBy&lt;/code&gt; on exactly one side.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.2 Keeping Both Sides in Sync
&lt;/h3&gt;

&lt;p&gt;In a bidirectional relationship you must update both sides programmatically in your helper methods, because the in-memory state is independent of the database state until flush:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add a convenience method on the owning side&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;enroll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStudents&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// keep inverse in sync&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;unenroll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStudents&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. equals() and hashCode() — The Critical Foundation
&lt;/h2&gt;

&lt;p&gt;Hibernate uses &lt;code&gt;equals()&lt;/code&gt; and &lt;code&gt;hashCode()&lt;/code&gt; to determine whether two entity instances represent the same row, especially when adding/removing from &lt;code&gt;Set&lt;/code&gt; collections and when merging detached entities. The &lt;strong&gt;default &lt;code&gt;Object&lt;/code&gt; identity implementation breaks all of this&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Correct Implementation (Business Key or UUID)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NaturalId&lt;/span&gt;                     &lt;span class="c1"&gt;// Hibernate annotation&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;courseCode&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// e.g. "CS-101" — stable business key&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;courseCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;courseCode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;courseCode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// must be stable across states&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — Using &lt;code&gt;id&lt;/code&gt; for &lt;code&gt;hashCode&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never base &lt;code&gt;hashCode&lt;/code&gt; on &lt;code&gt;@Id&lt;/code&gt; if entities can be in a &lt;code&gt;Set&lt;/code&gt; before being persisted. A transient entity has &lt;code&gt;id = null&lt;/code&gt;, so its &lt;code&gt;hashCode&lt;/code&gt; changes on persist, which &lt;strong&gt;silently corrupts&lt;/strong&gt; any &lt;code&gt;Set&lt;/code&gt; or &lt;code&gt;HashMap&lt;/code&gt; that contained it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Intermediate Entity for Join Table With Extra Columns
&lt;/h2&gt;

&lt;p&gt;When the join table needs to store data (enrollment date, grade, seat number, etc.), replace the &lt;code&gt;@ManyToMany&lt;/code&gt; shortcut with an &lt;strong&gt;explicit intermediate entity&lt;/strong&gt;. This is the most robust and recommended pattern in production systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 Composite Key Class
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Embeddable&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnrollmentId&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Serializable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;studentId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"course_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// equals() + hashCode() required for @Embeddable PKs&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2 Enrollment (Intermediate) Entity
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student_course"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Enrollment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@EmbeddedId&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;EnrollmentId&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EnrollmentId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@MapsId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"studentId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@MapsId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"courseId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;enrolledAt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;grade&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.3 Parent Entities
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orphanRemoval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Enrollment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;enrollments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;enroll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Enrollment&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Enrollment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setStudent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCourse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEnrolledAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;enrollments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"course"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// no cascade from Course side&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Enrollment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;enrollments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Best Practice — Cascade only from the aggregate root&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apply &lt;code&gt;CascadeType.ALL&lt;/code&gt; + &lt;code&gt;orphanRemoval&lt;/code&gt; only on the owning aggregate root side (&lt;code&gt;Student&lt;/code&gt;). Do &lt;strong&gt;not&lt;/strong&gt; cascade from &lt;code&gt;Course&lt;/code&gt; — it is a separate aggregate and should not delete enrollments when a course is touched.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. Fetch Strategies &amp;amp; The N+1 Problem
&lt;/h2&gt;

&lt;p&gt;Fetch strategy is the single most impactful performance decision in any JPA application.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 Always Use LAZY — Never EAGER
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Correct — LAZY is the safe default&lt;/span&gt;
&lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Wrong — loads ALL courses for ALL students every time a Student is loaded&lt;/span&gt;
&lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EAGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.2 Solving N+1 With JOIN FETCH
&lt;/h3&gt;

&lt;p&gt;Even with LAZY loading, iterating a collection inside a loop produces one SQL query per iteration. Fix this with a &lt;code&gt;JOIN FETCH&lt;/code&gt; JPQL query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// N+1 — fires one extra query per student&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;em&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT s FROM Student s"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResultList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCourses&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// N hits&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Fixed — single JOIN query&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;em&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"SELECT DISTINCT s FROM Student s JOIN FETCH s.courses"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getResultList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.3 Using @BatchSize as a Middle Ground
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@BatchSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// loads 25 students' courses in one IN (...) query&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — &lt;code&gt;MultipleBagFetchException&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You cannot &lt;code&gt;JOIN FETCH&lt;/code&gt; two &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; collections in the same JPQL query. Hibernate throws &lt;code&gt;MultipleBagFetchException&lt;/code&gt;. &lt;strong&gt;Fix:&lt;/strong&gt; change both to &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt;, or fetch one in JPQL and use &lt;code&gt;@BatchSize&lt;/code&gt; for the second.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  8. Cascade Types — What to Use and When
&lt;/h2&gt;

&lt;p&gt;Cascade types control which JPA lifecycle operations (&lt;code&gt;PERSIST&lt;/code&gt;, &lt;code&gt;MERGE&lt;/code&gt;, &lt;code&gt;REMOVE&lt;/code&gt;, etc.) are propagated from parent to child.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 Recommended Cascade Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Cascade&lt;/th&gt;
&lt;th&gt;orphanRemoval&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple &lt;code&gt;@ManyToMany&lt;/code&gt; (no extra cols)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PERSIST, MERGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Do &lt;strong&gt;NOT&lt;/strong&gt; use &lt;code&gt;REMOVE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intermediate entity (owned)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only from aggregate root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intermediate entity (shared)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PERSIST, MERGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shared = don't remove&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Course → Enrollment&lt;/code&gt; (inverse)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(none)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Let &lt;code&gt;Student&lt;/code&gt; own it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; on &lt;code&gt;@ManyToMany&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; (or &lt;code&gt;ALL&lt;/code&gt;) on a plain &lt;code&gt;@ManyToMany&lt;/code&gt; will delete the &lt;strong&gt;related entities themselves&lt;/strong&gt; — not just the join row. Removing one &lt;code&gt;Student&lt;/code&gt; will delete all their &lt;code&gt;Course&lt;/code&gt; records from the courses table, affecting every other enrolled student.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. Serialization — Avoiding Infinite Recursion
&lt;/h2&gt;

&lt;p&gt;Bidirectional relationships create circular object graphs. When Jackson (or any JSON library) tries to serialize a &lt;code&gt;Student&lt;/code&gt; that contains &lt;code&gt;Courses&lt;/code&gt;, which contain &lt;code&gt;Students&lt;/code&gt;, which contain &lt;code&gt;Courses&lt;/code&gt;... it throws a &lt;code&gt;StackOverflowError&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.1 Jackson Annotations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// On the owning side (Student)&lt;/span&gt;
&lt;span class="nd"&gt;@JsonManagedReference&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// On the inverse side (Course)&lt;/span&gt;
&lt;span class="nd"&gt;@JsonBackReference&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// this side is NOT serialized&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.2 Better: Use DTOs (Recommended)
&lt;/h3&gt;

&lt;p&gt;Never serialize JPA entities directly to your API layer. Use dedicated DTO/response classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DTO — safe, no cycles, no Hibernate proxies&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;CourseDTO&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;credits&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;CourseDTO&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CourseDTO&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTitle&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCredits&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;StudentDTO&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CourseDTO&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;StudentDTO&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StudentDTO&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCourses&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;CourseDTO:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toSet&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Performance Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  10.1 Projections and DTO Queries
&lt;/h3&gt;

&lt;p&gt;For read-heavy endpoints, skip entity loading entirely and query directly into DTOs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT new com.example.dto.StudentCourseDTO(s.name, c.title) "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
       &lt;span class="s"&gt;"FROM Student s JOIN s.courses c WHERE s.id = :studentId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StudentCourseDTO&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findCoursesByStudent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Param&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"studentId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10.2 Pagination — Never Paginate With JOIN FETCH
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Wrong — Hibernate loads ALL rows into memory, then paginates&lt;/span&gt;
&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT DISTINCT s FROM Student s JOIN FETCH s.courses"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pageable&lt;/span&gt; &lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// issues HHH90003004 warning&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Correct — paginate the root entity, load collection separately&lt;/span&gt;
&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SELECT s FROM Student s"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;countQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SELECT COUNT(s) FROM Student s"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pageable&lt;/span&gt; &lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Then use @BatchSize or a second query to load courses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10.3 Use @Transactional on Service, Not Repository
&lt;/h3&gt;

&lt;p&gt;Keep your transactions at the service layer where the full unit of work is clear. Opening a transaction in a repository method gives you no control over lazy loading in the service.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Quick Reference — Best Practices vs Pitfalls
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Best Practice&lt;/th&gt;
&lt;th&gt;❌ Pitfall to Avoid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;@ManyToMany&lt;/code&gt; with intermediate entity for extra columns&lt;/td&gt;
&lt;td&gt;Using plain &lt;code&gt;@JoinTable&lt;/code&gt; when join table has extra data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set &lt;code&gt;fetch = FetchType.LAZY&lt;/code&gt; on both sides&lt;/td&gt;
&lt;td&gt;Using &lt;code&gt;FetchType.EAGER&lt;/code&gt; (causes N+1 queries)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Define owning side clearly with &lt;code&gt;mappedBy&lt;/code&gt; on inverse&lt;/td&gt;
&lt;td&gt;Bidirectional mapping without &lt;code&gt;mappedBy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; instead of &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; to avoid duplicates&lt;/td&gt;
&lt;td&gt;Using &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; and getting &lt;code&gt;MultipleBagFetchException&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;orphanRemoval&lt;/code&gt; + &lt;code&gt;CascadeType.ALL&lt;/code&gt; on parent side only&lt;/td&gt;
&lt;td&gt;Cascading &lt;code&gt;ALL&lt;/code&gt; on both sides (infinite loops / dual deletes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implement &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; based on business key&lt;/td&gt;
&lt;td&gt;Using default &lt;code&gt;Object&lt;/code&gt; identity for &lt;code&gt;equals&lt;/code&gt;/&lt;code&gt;hashCode&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;@BatchSize&lt;/code&gt; or &lt;code&gt;JOIN FETCH&lt;/code&gt; to load related data&lt;/td&gt;
&lt;td&gt;Loading collections in a loop (classic N+1 problem)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use DTOs and projections for read-heavy queries&lt;/td&gt;
&lt;td&gt;Serializing full entity graphs to JSON (&lt;code&gt;StackOverflow&lt;/code&gt; risk)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  12. Conclusion
&lt;/h2&gt;

&lt;p&gt;Many-to-many associations are powerful but require deliberate design. The key takeaways are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; — always.&lt;/strong&gt; &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; in M:N leads to bags, duplicates, and &lt;code&gt;MultipleBagFetchException&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer intermediate entity&lt;/strong&gt; — as soon as the join table has any extra column, model it explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep &lt;code&gt;fetch=LAZY&lt;/code&gt; everywhere&lt;/strong&gt; — solve loading problems with &lt;code&gt;JOIN FETCH&lt;/code&gt; or &lt;code&gt;@BatchSize&lt;/code&gt;, not &lt;code&gt;EAGER&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; on a stable business key&lt;/strong&gt; — never rely on the database-generated &lt;code&gt;id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cascade carefully&lt;/strong&gt; — &lt;code&gt;REMOVE&lt;/code&gt; and &lt;code&gt;orphanRemoval&lt;/code&gt; belong only on aggregate-root-owned children.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use DTOs at the API layer&lt;/strong&gt; — never serialize entity graphs directly to JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure first&lt;/strong&gt; — use Hibernate's statistics or a query logger (P6Spy/datasource-proxy) to confirm you have no N+1 queries before shipping.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hibernate</category>
      <category>jpa</category>
      <category>springboot</category>
      <category>java</category>
    </item>
    <item>
      <title>JPA Mapping with Hibernate- One-to-Many and Many-to-One Relationship</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 14 Mar 2026 11:14:02 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-many-and-many-to-one-relationship-38nf</link>
      <guid>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-many-and-many-to-one-relationship-38nf</guid>
      <description>&lt;p&gt;In the previous section, we discussed the &lt;a href="https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-one-relationship-g41"&gt;One-to-One Relationship&lt;/a&gt; Now, let’s look at the one-to-many and many-to-one relationships&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What Are These Relationships?&lt;/li&gt;
&lt;li&gt;
Setting Up a Bidirectional Relationship

&lt;ul&gt;
&lt;li&gt;The Parent (One Side)&lt;/li&gt;
&lt;li&gt;The Child (Many Side)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Unidirectional @OneToMany — Avoid It&lt;/li&gt;
&lt;li&gt;
Best Practices

&lt;ul&gt;
&lt;li&gt;1. Always Use Lazy Fetching&lt;/li&gt;
&lt;li&gt;2. Use List Instead of Set&lt;/li&gt;
&lt;li&gt;3. Keep Both Sides in Sync&lt;/li&gt;
&lt;li&gt;4. Use orphanRemoval for Parent-Owned Children&lt;/li&gt;
&lt;li&gt;5. Never Cascade on @ManyToOne&lt;/li&gt;
&lt;li&gt;6. Override equals and hashCode&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Common Pitfalls

&lt;ul&gt;
&lt;li&gt;The N+1 Query Problem&lt;/li&gt;
&lt;li&gt;LazyInitializationException&lt;/li&gt;
&lt;li&gt;Out-of-Sync Bidirectional State&lt;/li&gt;
&lt;li&gt;CascadeType.REMOVE + orphanRemoval Redundancy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cascade Types Reference&lt;/li&gt;
&lt;li&gt;Quick Reference Table&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. What Are These Relationships?
&lt;/h2&gt;

&lt;p&gt;In JPA, &lt;strong&gt;One-to-Many&lt;/strong&gt; and &lt;strong&gt;Many-to-One&lt;/strong&gt; are two sides of the same coin. A &lt;code&gt;Department&lt;/code&gt; can have many &lt;code&gt;Employee&lt;/code&gt; records, and each &lt;code&gt;Employee&lt;/code&gt; belongs to one &lt;code&gt;Department&lt;/code&gt;. In the database, the foreign key (&lt;code&gt;department_id&lt;/code&gt;) lives on the &lt;code&gt;employees&lt;/code&gt; table — making &lt;code&gt;Employee&lt;/code&gt; the &lt;strong&gt;owning side&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Terminology
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Owning side&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The entity that holds the foreign key column in the database — always the &lt;code&gt;@ManyToOne&lt;/code&gt; side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inverse side&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The entity with &lt;code&gt;mappedBy&lt;/code&gt; — does not control the FK, just navigates the relationship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Used on the inverse (non-owning) side to point back to the owning field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;CascadeType&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Which operations (&lt;code&gt;PERSIST&lt;/code&gt;, &lt;code&gt;MERGE&lt;/code&gt;, &lt;code&gt;REMOVE&lt;/code&gt;, etc.) propagate from parent to child&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;orphanRemoval&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatically deletes a child row when it is removed from the parent collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;FetchType.LAZY&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Child data is loaded on demand when accessed — default for collections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;FetchType.EAGER&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Child data is always loaded with the parent — default for single associations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Database Representation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;departments            employees
────────────────       ──────────────────────
dept_id  (PK)    ◀─    employee_id  (PK)
name                   name
                       department_id  (FK)  ← FK lives here (owning side)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Setting Up a Bidirectional Relationship
&lt;/h2&gt;

&lt;p&gt;The recommended approach is a &lt;strong&gt;bidirectional&lt;/strong&gt; mapping, where both entities know about each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Parent (One Side)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orphanRemoval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Always use helper methods to keep both sides in sync&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;removeEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Child (Many Side)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// Always override the default!&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;mappedBy&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
It tells JPA that &lt;code&gt;Department&lt;/code&gt; is the &lt;em&gt;inverse&lt;/em&gt; side — it does not own the FK column. Without it, Hibernate creates a join table, which is almost never what you want.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Unidirectional @OneToMany — Avoid It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Looks clean but generates extra SQL&lt;/span&gt;
&lt;span class="nd"&gt;@OneToMany&lt;/span&gt;
&lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// no mappedBy&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;Department&lt;/code&gt; knows about &lt;code&gt;Employee&lt;/code&gt;, but &lt;code&gt;Employee&lt;/code&gt; does not reference &lt;code&gt;Department&lt;/code&gt;. When Hibernate cannot write the FK during the child &lt;code&gt;INSERT&lt;/code&gt; (because it doesn't own the column), it issues &lt;strong&gt;extra &lt;code&gt;UPDATE&lt;/code&gt; statements&lt;/strong&gt; afterward. This produces unnecessary SQL and hurts performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems with unidirectional &lt;code&gt;@OneToMany&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates extra &lt;code&gt;UPDATE&lt;/code&gt; queries on every insert&lt;/li&gt;
&lt;li&gt;Poor performance at scale&lt;/li&gt;
&lt;li&gt;May silently create join tables if &lt;code&gt;@JoinColumn&lt;/code&gt; is omitted&lt;/li&gt;
&lt;li&gt;Harder to maintain relationship consistency&lt;/li&gt;
&lt;li&gt;Limited query capability from the child side&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Best Practice:&lt;/strong&gt; Always use &lt;code&gt;@ManyToOne&lt;/code&gt; as the owning side and &lt;code&gt;@OneToMany(mappedBy = "...")&lt;/code&gt; as the inverse side for bidirectional relationships.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Always Use Lazy Fetching
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@ManyToOne&lt;/code&gt; defaults to &lt;code&gt;EAGER&lt;/code&gt; — a hidden performance trap that loads the parent every time you load a child, even when you don't need it. Always override it explicitly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// override the EAGER default&lt;/span&gt;
&lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@OneToMany&lt;/code&gt; is lazy by default, which is correct — leave it as-is.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use &lt;code&gt;List&lt;/code&gt; Instead of &lt;code&gt;Set&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If a JPA entity’s equals() and hashCode() are based on an auto-generated identifier, developers often use a constant hashCode() until the ID is assigned.&lt;/p&gt;

&lt;p&gt;However, this hurts Set performance because all elements end up in the same hash bucket. As a result, every add() or contains() operation requires scanning all elements, degrading performance from O(1) to O(n).&lt;/p&gt;

&lt;p&gt;In a Set, duplicate elements are not allowed because it relies on equals() and hashCode() to determine uniqueness. However, in Jakarta Persistence entities, equality typically corresponds to the identity of the associated database record. Since every child entity has a unique primary key (or a unique business key), entities retrieved from the database will already be distinct.&lt;br&gt;
Because of this, a Set rarely provides any real benefit for a bidirectional @OneToMany association. Hibernate will not return duplicate rows for the same entity when loading the collection, so the collection naturally contains unique elements.&lt;/p&gt;

&lt;p&gt;From a performance perspective, List implementations such as ArrayList are usually faster and simpler.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Keep Both Sides in Sync
&lt;/h3&gt;

&lt;p&gt;In a bidirectional relationship, JPA uses the &lt;em&gt;owning side&lt;/em&gt; to write to the DB. If you only update the inverse side (&lt;code&gt;Department.employees&lt;/code&gt;), the FK won't be persisted. Always use helper methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Good — updates both sides&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Bad — only updates the inverse side, FK not persisted&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Use &lt;code&gt;orphanRemoval&lt;/code&gt; for Parent-Owned Children
&lt;/h3&gt;

&lt;p&gt;When the parent fully owns the lifecycle of its children, enable &lt;code&gt;orphanRemoval&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orphanRemoval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Removing a child from the collection now &lt;strong&gt;automatically deletes it&lt;/strong&gt; from the database on the next flush — no need for an explicit &lt;code&gt;em.remove()&lt;/code&gt; call.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Never Cascade on &lt;code&gt;@ManyToOne&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cascade = CascadeType.ALL&lt;/code&gt; belongs on the parent (&lt;code&gt;@OneToMany&lt;/code&gt;) side. Adding it to &lt;code&gt;@ManyToOne&lt;/code&gt; can cause &lt;strong&gt;catastrophic side effects&lt;/strong&gt; — like deleting a &lt;code&gt;Department&lt;/code&gt; when you delete one &lt;code&gt;Employee&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ WRONG — could delete the entire department!&lt;/span&gt;
&lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ CORRECT — no cascade on the child side&lt;/span&gt;
&lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Override &lt;code&gt;equals&lt;/code&gt; and &lt;code&gt;hashCode&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Use a natural &lt;strong&gt;business key&lt;/strong&gt; (e.g., &lt;code&gt;email&lt;/code&gt; for an employee), not the database ID. The ID is &lt;code&gt;null&lt;/code&gt; before the entity is persisted, so ID-based equality breaks collections like &lt;code&gt;HashSet&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// stable across transient and persistent states&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Common Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The N+1 Query Problem
&lt;/h3&gt;

&lt;p&gt;The most common JPA performance issue. Loading a list of departments, then accessing each one's &lt;code&gt;employees&lt;/code&gt;, fires &lt;strong&gt;one query per department&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Triggers 1 + N queries&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;depts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;depts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// N extra queries!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix 1 — &lt;code&gt;JOIN FETCH&lt;/code&gt; in JPQL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.employees"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAllWithEmployees&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix 2 — &lt;code&gt;@EntityGraph&lt;/code&gt; (Spring Data):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@EntityGraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributePaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"employees"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix 3 — &lt;code&gt;@BatchSize&lt;/code&gt; (Hibernate):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@BatchSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@BatchSize&lt;/code&gt; loads children in batches using an &lt;code&gt;IN (...)&lt;/code&gt; clause rather than one query each — a low-friction option when you don't want to change your JPQL queries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;LazyInitializationException&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Accessing a lazy collection &lt;strong&gt;outside of an active Hibernate session&lt;/strong&gt; (e.g., after the transaction has closed) throws this exception.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Fails if the session is already closed&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// LazyInitializationException!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always access lazy relationships within a &lt;code&gt;@Transactional&lt;/code&gt; context, or eagerly fetch what you need in the repository query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Safe — transaction is open for the duration of the method&lt;/span&gt;
&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;DepartmentDTO&lt;/span&gt; &lt;span class="nf"&gt;getDepartmentWithEmployees&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;dept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;dept&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// safe here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DepartmentDTO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dept&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Out-of-Sync Bidirectional State
&lt;/h3&gt;

&lt;p&gt;Setting the child's reference without updating the parent's collection (or vice versa) leaves the &lt;strong&gt;in-memory object graph inconsistent&lt;/strong&gt;, even if the DB is correct after flush.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Only sets one side — department.getEmployees() won't contain emp in memory&lt;/span&gt;
&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Use the helper method to keep both sides consistent&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; + &lt;code&gt;orphanRemoval&lt;/code&gt; Redundancy
&lt;/h3&gt;

&lt;p&gt;Both cause child deletion when the parent is removed. Using both together is redundant and signals a misunderstanding of their purpose.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CascadeType.REMOVE&lt;/code&gt;&lt;/strong&gt; — deletes children when the parent entity is explicitly removed via &lt;code&gt;em.remove()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;orphanRemoval = true&lt;/code&gt;&lt;/strong&gt; — deletes children when they are removed from the parent's collection &lt;em&gt;or&lt;/em&gt; when the parent is deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Use &lt;code&gt;orphanRemoval = true&lt;/code&gt;&lt;/strong&gt; when the parent fully owns the child lifecycle — it covers the &lt;code&gt;REMOVE&lt;/code&gt; case and also handles disassociation from the collection. You do not need both.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Cascade Types Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cascade Type&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;th&gt;Use When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PERSIST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is saved for the first time, unsaved children are also saved&lt;/td&gt;
&lt;td&gt;Always safe on parent side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MERGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is merged (updated), children are also merged&lt;/td&gt;
&lt;td&gt;Almost always paired with &lt;code&gt;PERSIST&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REMOVE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is deleted, all children are deleted&lt;/td&gt;
&lt;td&gt;Only on owned children; use &lt;code&gt;orphanRemoval&lt;/code&gt; instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REFRESH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is refreshed from DB, children are also refreshed&lt;/td&gt;
&lt;td&gt;Rarely needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DETACH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is detached from context, children are also detached&lt;/td&gt;
&lt;td&gt;Rarely needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shorthand for all of the above&lt;/td&gt;
&lt;td&gt;Convenient but &lt;strong&gt;potentially dangerous&lt;/strong&gt; — review carefully&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  7. Quick Reference Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Recommended Setting&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@OneToMany&lt;/code&gt; fetch&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LAZY&lt;/code&gt; &lt;em&gt;(default)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Don't override unless necessary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@ManyToOne&lt;/code&gt; fetch&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LAZY&lt;/code&gt; &lt;em&gt;(explicit)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Default is &lt;code&gt;EAGER&lt;/code&gt; — &lt;strong&gt;always override&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;On the &lt;code&gt;@OneToMany&lt;/code&gt; side&lt;/td&gt;
&lt;td&gt;Marks the inverse (non-owning) side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cascade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CascadeType.ALL&lt;/code&gt; on parent only&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Never&lt;/strong&gt; put cascade on &lt;code&gt;@ManyToOne&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orphanRemoval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; for fully owned children&lt;/td&gt;
&lt;td&gt;Handles both removal and disassociation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Set&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safer than &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; with multiple joins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1 prevention&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;JOIN FETCH&lt;/code&gt; or &lt;code&gt;@EntityGraph&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@BatchSize&lt;/code&gt; is a low-friction alternative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;equals&lt;/code&gt;/&lt;code&gt;hashCode&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Based on business key&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Never&lt;/strong&gt; based on database ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unidirectional &lt;code&gt;@OneToMany&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Avoid&lt;/td&gt;
&lt;td&gt;Extra &lt;code&gt;UPDATE&lt;/code&gt; SQL, harder to maintain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Helper methods&lt;/td&gt;
&lt;td&gt;Always define on parent&lt;/td&gt;
&lt;td&gt;Keep both sides of bidirectional mapping in sync&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  8. Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;FK lives on the many side&lt;/strong&gt; — that entity is the &lt;strong&gt;owning side&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;mappedBy&lt;/code&gt; on &lt;code&gt;@OneToMany&lt;/code&gt; to declare the inverse side and avoid a spurious join table.&lt;/li&gt;
&lt;li&gt;Always set &lt;code&gt;@ManyToOne(fetch = FetchType.LAZY)&lt;/code&gt; — the default &lt;code&gt;EAGER&lt;/code&gt; is a performance trap.&lt;/li&gt;
&lt;li&gt;Write &lt;strong&gt;bidirectional helper methods&lt;/strong&gt; (&lt;code&gt;addEmployee&lt;/code&gt; / &lt;code&gt;removeEmployee&lt;/code&gt;) to keep both sides in sync.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; over &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; to avoid Cartesian products when joining multiple collections.&lt;/li&gt;
&lt;li&gt;Watch for the &lt;strong&gt;N+1 problem&lt;/strong&gt; whenever you iterate over collections — fix with &lt;code&gt;JOIN FETCH&lt;/code&gt;, &lt;code&gt;@EntityGraph&lt;/code&gt;, or &lt;code&gt;@BatchSize&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never cascade&lt;/strong&gt; from child to parent (&lt;code&gt;@ManyToOne&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;orphanRemoval = true&lt;/code&gt; over &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; — it handles more cases cleanly.&lt;/li&gt;
&lt;li&gt;Base &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; on a &lt;strong&gt;stable business key&lt;/strong&gt;, never on the auto-generated database ID.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Getter&lt;/span&gt;
&lt;span class="nd"&gt;@Setter&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orphanRemoval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Always use helper methods to keep both sides in sync&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;removeEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Getter&lt;/span&gt;
&lt;span class="nd"&gt;@Setter&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// Always override the default!&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"department_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;DepartmentRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EmployeeRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRunner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;DepartmentRepository&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;EmployeeRepository&lt;/span&gt; &lt;span class="n"&gt;employeeRepo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DataLoader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DepartmentRepository&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;EmployeeRepository&lt;/span&gt; &lt;span class="n"&gt;employeeRepo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;departmentRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;employeeRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employeeRepo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApplicationArguments&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 1: Create a Department entity
         * No DB query yet because the entity is only created in memory.
         */&lt;/span&gt;
        &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;engineering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Engineering"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 2: Create Employee entities
         * These are also only in memory at this point.
         */&lt;/span&gt;
        &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;alice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;bob&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;   &lt;span class="s"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 3: Link employees to department
         * The helper method usually:
         * 1. Adds employee to department.employees list
         * 2. Sets employee.department = this
         *
         * This ensures both sides of the bidirectional relationship stay consistent.
         */&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 4: Persist department
         *
         * Because the Department entity likely has:
         *
         * @OneToMany(mappedBy="department", cascade = CascadeType.ALL, orphanRemoval = true)
         *
         * Hibernate will cascade the persist operation to Employee entities.
         *
         * Expected SQL queries:
         *
         * INSERT INTO department (name)
         * INSERT INTO employee (name, email, department_id)
         * INSERT INTO employee (name, email, department_id)
         *
         * Total queries = 3
         */&lt;/span&gt;
        &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 5: Read all departments
         *
         * Expected SQL:
         * SELECT * FROM department
         *
         * If employees collection is LAZY (recommended),
         * employees are NOT fetched until accessed.
         */&lt;/span&gt;
        &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dept: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;

        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 6: Find department by ID
         *
         * Expected SQL:
         * SELECT * FROM department WHERE id = ?
         */&lt;/span&gt;
        &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;dept&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ifPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Found: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Department loaded"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// employees should load only here&lt;/span&gt;
        &lt;span class="n"&gt;dept&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;


        &lt;span class="cm"&gt;/*
         * STEP 7: Update employee
         *
         * Changing Alice's name marks the entity as dirty.
         *
         * Expected SQL on flush/commit:
         * UPDATE employee SET name='Alice Smith' WHERE id=?
         */&lt;/span&gt;
        &lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice Smith"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;employeeRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 8: Remove employee from department
         *
         * Helper method typically:
         * 1. Removes Bob from department.employees list
         * 2. Sets bob.department = null
         *
         * Because orphanRemoval = true:
         * Hibernate will DELETE the employee record automatically.
         *
         * Expected SQL:
         * DELETE FROM employee WHERE id = ?
         */&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * Saving department ensures Hibernate detects the orphan removal.
         */&lt;/span&gt;
        &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>java</category>
      <category>hibernate</category>
      <category>jpa</category>
      <category>springboot</category>
    </item>
    <item>
      <title>JPA Mapping with Hibernate-One-to-One Relationship</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 07 Mar 2026 12:46:24 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-one-relationship-g41</link>
      <guid>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-one-relationship-g41</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is JPA?&lt;/li&gt;
&lt;li&gt;What is Hibernate?&lt;/li&gt;
&lt;li&gt;JPA vs Hibernate — Key Differences&lt;/li&gt;
&lt;li&gt;Relationship Mapping in JPA — Quick Reference&lt;/li&gt;
&lt;li&gt;
What is a One-to-One Relationship?

&lt;ul&gt;
&lt;li&gt;Unidirectional vs Bidirectional&lt;/li&gt;
&lt;li&gt;How to Identify Owner, FK Side, and Child&lt;/li&gt;
&lt;li&gt;JPA Annotation Summary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Bidirectional One-to-One — Standard Mapping

&lt;ul&gt;
&lt;li&gt;The Child Entity (Owning Side)&lt;/li&gt;
&lt;li&gt;The Parent Entity (Inverse Side)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
The Lazy Loading Problem on the Inverse Side

&lt;ul&gt;
&lt;li&gt;Why the Extra Query Fires&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Solutions to the Inverse-Side Probe Query

&lt;ul&gt;
&lt;li&gt;Option 1 — @LazyToOne Bytecode Instrumentation&lt;/li&gt;
&lt;li&gt;Option 2 — JOIN FETCH Query&lt;/li&gt;
&lt;li&gt;Option 3 — @NamedEntityGraph&lt;/li&gt;
&lt;li&gt;Option 4 — @MapsId Shared Primary Key (Best &amp;amp; Simplest)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Deep Dive — @MapsId

&lt;ul&gt;
&lt;li&gt;Advantages of @MapsId&lt;/li&gt;
&lt;li&gt;Downsides of @MapsId&lt;/li&gt;
&lt;li&gt;When @MapsId Is Ideal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Comparison — @JoinColumn vs @MapsId&lt;/li&gt;
&lt;li&gt;Quick Reference Table&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. What is JPA?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Java Persistence API (JPA)&lt;/strong&gt; is a &lt;strong&gt;Java specification&lt;/strong&gt; that defines a standard way to manage relational data in Java applications using &lt;strong&gt;Object Relational Mapping (ORM)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It provides a set of &lt;strong&gt;interfaces and annotations&lt;/strong&gt; that allow developers to map Java objects to database tables, perform CRUD operations, and manage persistence without writing large amounts of SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JPA is a &lt;strong&gt;specification, not an implementation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It standardizes how Java applications interact with relational databases&lt;/li&gt;
&lt;li&gt;It uses &lt;strong&gt;annotations and configuration&lt;/strong&gt; to map objects to tables&lt;/li&gt;
&lt;li&gt;It simplifies database operations through &lt;strong&gt;entity management and persistence context&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. What is Hibernate?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hibernate&lt;/strong&gt; is a popular &lt;strong&gt;open-source ORM framework&lt;/strong&gt; that provides a concrete implementation of the &lt;strong&gt;JPA specification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It allows developers to interact with databases using &lt;strong&gt;Java objects instead of writing complex SQL queries&lt;/strong&gt;, making application code &lt;strong&gt;loosely coupled&lt;/strong&gt; with the underlying database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hibernate is a &lt;strong&gt;JPA implementation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Provides powerful ORM capabilities beyond the JPA spec&lt;/li&gt;
&lt;li&gt;Handles &lt;strong&gt;CRUD operations automatically&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Supports &lt;strong&gt;caching, lazy loading, and transaction management&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reduces boilerplate JDBC code&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. JPA vs Hibernate — Key Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;JPA&lt;/th&gt;
&lt;th&gt;Hibernate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;Specification (standard API)&lt;/td&gt;
&lt;td&gt;Implementation of JPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who defines it&lt;/td&gt;
&lt;td&gt;Jakarta EE / Oracle&lt;/td&gt;
&lt;td&gt;Red Hat / JBoss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can run standalone?&lt;/td&gt;
&lt;td&gt;No — needs an implementation&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra features&lt;/td&gt;
&lt;td&gt;Standard only&lt;/td&gt;
&lt;td&gt;Caching, &lt;code&gt;@BatchSize&lt;/code&gt;, &lt;code&gt;@NaturalId&lt;/code&gt;, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code coupling&lt;/td&gt;
&lt;td&gt;Low — portable across providers&lt;/td&gt;
&lt;td&gt;Higher — Hibernate-specific annotations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers write code using &lt;strong&gt;JPA annotations and interfaces&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hibernate executes the actual database operations&lt;/strong&gt; behind the scenes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Relationship Mapping in JPA — Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Annotation&lt;/th&gt;
&lt;th&gt;Relationship&lt;/th&gt;
&lt;th&gt;FK Location&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@OneToOne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One entity ↔ one entity&lt;/td&gt;
&lt;td&gt;Child / owning side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@OneToMany&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One entity → many entities&lt;/td&gt;
&lt;td&gt;Child table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ManyToOne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Many entities → one entity&lt;/td&gt;
&lt;td&gt;Owning entity (FK here)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ManyToMany&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Many entities ↔ many entities&lt;/td&gt;
&lt;td&gt;Join / junction table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key best practices across all relationship types:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always use &lt;code&gt;FetchType.LAZY&lt;/code&gt;&lt;/strong&gt; on relationships — avoids N+1 query problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt; goes on the &lt;em&gt;non-owning&lt;/em&gt; side (the side without the FK column)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cascade carefully&lt;/strong&gt; — only cascade from parent to child, never upward&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@JoinColumn&lt;/code&gt;&lt;/strong&gt; explicitly names your FK column for clarity&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;&lt;code&gt;Set&lt;/code&gt;&lt;/strong&gt; instead of &lt;code&gt;List&lt;/code&gt; for &lt;code&gt;@ManyToMany&lt;/code&gt; to avoid duplicate join queries&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. What is a One-to-One Relationship?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;One-to-One relationship&lt;/strong&gt; occurs when &lt;strong&gt;one entity is associated with exactly one instance of another entity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Person&lt;/code&gt; → &lt;code&gt;Passport&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;User&lt;/code&gt; → &lt;code&gt;UserProfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Order&lt;/code&gt; → &lt;code&gt;Invoice&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unidirectional vs Bidirectional
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Unidirectional&lt;/strong&gt; — Only one entity has a reference to the other. Navigation works in one direction only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User  →  UserProfile      (User knows about UserProfile; UserProfile does NOT know about User)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bidirectional&lt;/strong&gt; — Both entities have a reference to each other. Navigation works in both directions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User  ⇆  UserProfile      (both sides can navigate to the other)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to Identify Owner, FK Side, and Child
&lt;/h3&gt;

&lt;p&gt;Ask yourself these three questions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who cannot exist without the other?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;That's the &lt;strong&gt;child&lt;/strong&gt; → holds the FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who exists independently?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;That's the &lt;strong&gt;parent&lt;/strong&gt; → has &lt;code&gt;mappedBy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who makes sense to delete first?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;That's the &lt;strong&gt;child&lt;/strong&gt; → cascade from parent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; Child = cannot exist without parent = has &lt;code&gt;@JoinColumn&lt;/code&gt; (FK).&lt;br&gt;
Parent = exists independently = has &lt;code&gt;mappedBy&lt;/code&gt; (no FK).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  JPA Annotation Summary
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Annotation&lt;/th&gt;
&lt;th&gt;Side&lt;/th&gt;
&lt;th&gt;FK&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@JoinColumn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Owning / Child&lt;/td&gt;
&lt;td&gt;FK lives here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inverse / Parent&lt;/td&gt;
&lt;td&gt;No FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@JoinTable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@ManyToMany&lt;/code&gt; owner&lt;/td&gt;
&lt;td&gt;Junction table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cascade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parent → Child&lt;/td&gt;
&lt;td&gt;Delete parent = delete child&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  6. Bidirectional One-to-One — Standard Mapping
&lt;/h2&gt;

&lt;p&gt;In a &lt;code&gt;User ↔ UserProfile&lt;/code&gt; relationship, &lt;code&gt;UserProfile&lt;/code&gt; is the &lt;strong&gt;child&lt;/strong&gt; because the foreign key (&lt;code&gt;user_id&lt;/code&gt;) lives in the &lt;code&gt;user_profile&lt;/code&gt; table. &lt;code&gt;User&lt;/code&gt; is the &lt;strong&gt;parent&lt;/strong&gt; (inverse side).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users                  user_profile
─────────────          ──────────────────────
id  (PK)        ◀──    id  (PK)
username               phone
password               address
enabled                user_id  (FK) ← FK lives here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Child Entity (Owning Side)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@JoinColumn&lt;/code&gt; is used on the owning side — the child entity contains the foreign key pointing to the parent's primary key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_profile"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// FK column lives here&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Parent Entity (Inverse Side)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ElementCollection&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EAGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@CollectionTable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_roles"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;joinColumns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;uniqueConstraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@UniqueConstraint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Enumerated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EnumType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Bidirectional sync helper — keeps both sides consistent&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setProfile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Role helper methods&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;removeRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;hasRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on &lt;code&gt;@ElementCollection&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;@ElementCollection&lt;/code&gt; stores simple values (like enum &lt;code&gt;Role&lt;/code&gt;) in a separate table — it is &lt;strong&gt;not&lt;/strong&gt; a full entity relationship, so there is no "other side" to sync.&lt;br&gt;
Keeping &lt;code&gt;FetchType.EAGER&lt;/code&gt; here is intentional when using &lt;strong&gt;Spring Security&lt;/strong&gt; — &lt;code&gt;UserDetails&lt;/code&gt; needs roles immediately on authentication.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. The Lazy Loading Problem on the Inverse Side
&lt;/h2&gt;

&lt;p&gt;Declaring &lt;code&gt;fetch = FetchType.LAZY&lt;/code&gt; on the &lt;strong&gt;inverse side&lt;/strong&gt; (&lt;code&gt;mappedBy&lt;/code&gt;) of a &lt;code&gt;@OneToOne&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; actually make it lazy. Hibernate silently fires an extra query anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Extra Query Fires
&lt;/h3&gt;

&lt;p&gt;On the &lt;strong&gt;owning side&lt;/strong&gt;, the FK column is in the same row — Hibernate immediately knows if the association is &lt;code&gt;null&lt;/code&gt; or not, and can safely create a proxy.&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;inverse side&lt;/strong&gt;, there is no FK column. Hibernate must probe the database just to determine whether a &lt;code&gt;UserProfile&lt;/code&gt; exists for a given &lt;code&gt;User&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;          &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;user_profile&lt;/span&gt;   &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="n"&gt;YOU&lt;/span&gt; &lt;span class="n"&gt;DIDN&lt;/span&gt;&lt;span class="s1"&gt;'T ASK FOR THIS (Hibernate probe)
Query 3: SELECT * FROM user_roles     ← expected (EAGER @ElementCollection)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query 2 is Hibernate asking: &lt;em&gt;"does a profile exist for this user?"&lt;/em&gt; — because the inverse side has no FK column to check locally.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Solutions to the Inverse-Side Probe Query
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1 — @LazyToOne Bytecode Instrumentation
&lt;/h3&gt;

&lt;p&gt;Forces truly lazy loading via &lt;strong&gt;Hibernate bytecode enhancement&lt;/strong&gt; — Hibernate injects an interceptor into the bytecode so it never needs to probe the DB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// User.java&lt;/span&gt;
&lt;span class="nd"&gt;@OneToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@LazyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LazyToOneOption&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NO_PROXY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// forces true lazy via bytecode&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requires adding the &lt;strong&gt;Hibernate bytecode enhancer&lt;/strong&gt; plugin to your build tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Maven --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.hibernate.orm.tooling&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;hibernate-enhance-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;enableLazyInitialization&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enableLazyInitialization&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without Enhancement&lt;/th&gt;
&lt;th&gt;With Enhancement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hibernate probes DB to check if profile is null&lt;/td&gt;
&lt;td&gt;Bytecode interceptor handles it — no probe needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra query always fires&lt;/td&gt;
&lt;td&gt;Truly lazy — query only fires on access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Option 2 — JOIN FETCH Query
&lt;/h3&gt;

&lt;p&gt;Load both entities together in a &lt;strong&gt;single SQL query&lt;/strong&gt; — profile is already in memory, no probe needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT u FROM User u LEFT JOIN FETCH u.profile"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAllWithProfile&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hibernate generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;user_profile&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without JOIN FETCH&lt;/th&gt;
&lt;th&gt;With JOIN FETCH&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;SELECT FROM users&lt;/code&gt; + &lt;code&gt;SELECT FROM user_profile&lt;/code&gt; (2 queries)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SELECT FROM users LEFT JOIN user_profile&lt;/code&gt; (1 query)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Option 3 — @NamedEntityGraph
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;@NamedEntityGraph&lt;/code&gt; defines a &lt;strong&gt;pre-declared fetch plan&lt;/strong&gt; that can be reused across multiple repository methods, avoiding the need to write &lt;code&gt;JOIN FETCH&lt;/code&gt; everywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@NamedEntityGraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"User.withDetails"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributeNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedAttributeNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"roles"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedAttributeNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"profile"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it in the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT u FROM User u LEFT JOIN FETCH u.profile"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAllWithProfile&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@EntityGraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User.withDetails"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// applies the named graph&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Combining with &lt;code&gt;@NamedSubgraph&lt;/code&gt; for nested associations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you need to fetch deeply nested associations (e.g., &lt;code&gt;User → UserProfile → Address&lt;/code&gt;), use &lt;code&gt;@NamedSubgraph&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@NamedEntityGraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"User.profile"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributeNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedAttributeNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"profile"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subgraph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"profile-subgraph"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;subgraphs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedSubgraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"profile-subgraph"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;attributeNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;@NamedAttributeNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"address"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Repository&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@EntityGraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"User.profile"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EntityGraph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EntityGraphType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FETCH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the entity graph, Hibernate generates a &lt;strong&gt;single query&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;user_profile&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;       &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use &lt;code&gt;@EntityGraph&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;strong&gt;dynamic fetch strategies&lt;/strong&gt; per query&lt;/li&gt;
&lt;li&gt;You want associations to be &lt;strong&gt;LAZY by default&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You want to avoid scattering &lt;code&gt;JOIN FETCH&lt;/code&gt; across all queries&lt;/li&gt;
&lt;li&gt;Different repository methods need &lt;strong&gt;different fetch plans&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 4 — @MapsId Shared Primary Key (Best &amp;amp; Simplest)
&lt;/h3&gt;

&lt;p&gt;Since both entities &lt;strong&gt;share the same PK&lt;/strong&gt;, Hibernate already knows the profile ID without probing. This is the cleanest solution for a strict &lt;code&gt;@OneToOne&lt;/code&gt; relationship.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;See Section 9 — Deep Dive: @MapsId for full details.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. Deep Dive — @MapsId
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;best way to map a &lt;code&gt;@OneToOne&lt;/code&gt; relationship&lt;/strong&gt; in Hibernate is using &lt;strong&gt;&lt;code&gt;@MapsId&lt;/code&gt;&lt;/strong&gt;, which allows the child entity to &lt;strong&gt;share the same primary key as the parent entity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;child table uses the parent's PK as its own PK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;This avoids creating an additional FK column&lt;/li&gt;
&lt;li&gt;Hibernate already knows the child's ID — &lt;strong&gt;no probe query needed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users                  user_profile
─────────────          ──────────────────────
id  (PK)        ═══    id  (PK = FK) ← shared PK, no separate user_id column
username               phone
password               address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_profile"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;                          &lt;span class="c1"&gt;// No @GeneratedValue — value is copied from User&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@MapsId&lt;/span&gt;                      &lt;span class="c1"&gt;// this entity's PK = users.id&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; column in &lt;code&gt;user_profile&lt;/code&gt; serves as &lt;strong&gt;both PK and FK&lt;/strong&gt; — its value is copied directly from &lt;code&gt;User.id&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Differences Per Entity
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;User&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;UserProfile&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@GeneratedValue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes — generates own PK&lt;/td&gt;
&lt;td&gt;❌ No — copies from &lt;code&gt;User&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owns the relationship&lt;/td&gt;
&lt;td&gt;No — no &lt;code&gt;profile&lt;/code&gt; field needed&lt;/td&gt;
&lt;td&gt;Yes — has &lt;code&gt;@MapsId&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Probe query on fetch&lt;/td&gt;
&lt;td&gt;Gone — no inverse field&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Save strategy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;userRepository.save(user)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;userProfileRepository.save(profile)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Advantages of @MapsId
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No extra FK column&lt;/strong&gt; — cleaner schema, better normalization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No probe query&lt;/strong&gt; — Hibernate already knows the child's ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient joins&lt;/strong&gt; — PK = FK means the join is on the same column&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No need for bidirectional association&lt;/strong&gt; — &lt;code&gt;User&lt;/code&gt; can be unidirectional&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UserProfile&lt;/code&gt; can always be fetched directly using the &lt;code&gt;User&lt;/code&gt; ID&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Downsides of @MapsId
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling&lt;/strong&gt; — &lt;code&gt;UserProfile&lt;/code&gt; cannot exist without &lt;code&gt;User&lt;/code&gt; (shares PK)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insert order dependency&lt;/strong&gt; — &lt;code&gt;User&lt;/code&gt; must be persisted before &lt;code&gt;UserProfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less flexibility&lt;/strong&gt; — migrating from one-to-one → one-to-many requires schema redesign&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder independent lifecycle management&lt;/strong&gt; — deleting &lt;code&gt;User&lt;/code&gt; invalidates &lt;code&gt;UserProfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not suitable for optional relationships&lt;/strong&gt; — if a &lt;code&gt;User&lt;/code&gt; can exist without a &lt;code&gt;UserProfile&lt;/code&gt;, this gets awkward&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder for beginners&lt;/strong&gt; — shared PK, &lt;code&gt;@MapsId&lt;/code&gt;, and entity lifecycle can be confusing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema migration complexity&lt;/strong&gt; — adding a separate PK to the child table later requires a full refactor&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  When @MapsId Is Ideal
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;@MapsId&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The relationship is &lt;strong&gt;strictly one-to-one&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The child &lt;strong&gt;cannot exist without the parent&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The child is more like an &lt;strong&gt;extension of the parent&lt;/strong&gt; (e.g., &lt;code&gt;UserProfile&lt;/code&gt; is just extra columns for &lt;code&gt;User&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;zero extra queries&lt;/strong&gt; and a &lt;strong&gt;cleaner schema&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. Comparison — @JoinColumn vs @MapsId
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;@JoinColumn&lt;/code&gt; on child&lt;/th&gt;
&lt;th&gt;&lt;code&gt;@MapsId&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Profile FK column&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_profile.user_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;user_profile.id&lt;/code&gt; = &lt;code&gt;users.id&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hibernate knows child ID?&lt;/td&gt;
&lt;td&gt;Must probe DB&lt;/td&gt;
&lt;td&gt;Already has it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra query on &lt;code&gt;getUser()&lt;/code&gt;?&lt;/td&gt;
&lt;td&gt;Always fires (inverse side)&lt;/td&gt;
&lt;td&gt;Only when accessed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB schema&lt;/td&gt;
&lt;td&gt;Extra &lt;code&gt;user_id&lt;/code&gt; column&lt;/td&gt;
&lt;td&gt;Shared PK — cleaner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optional relationship?&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ Not ideal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Child exists independently?&lt;/td&gt;
&lt;td&gt;✅ Can&lt;/td&gt;
&lt;td&gt;❌ Cannot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema simplicity&lt;/td&gt;
&lt;td&gt;Slightly more columns&lt;/td&gt;
&lt;td&gt;Minimal columns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  11. Quick Reference Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Owning side&lt;/td&gt;
&lt;td&gt;Entity holding the FK (&lt;code&gt;@JoinColumn&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Always the child&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inverse side&lt;/td&gt;
&lt;td&gt;Entity with &lt;code&gt;mappedBy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No FK — navigation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@OneToOne&lt;/code&gt; fetch (owning)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FetchType.LAZY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FK in same row — proxy safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@OneToOne&lt;/code&gt; fetch (inverse)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;FetchType.LAZY&lt;/code&gt; declared, but won't be&lt;/td&gt;
&lt;td&gt;Hibernate probes regardless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Probe query fix&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@MapsId&lt;/code&gt; or &lt;code&gt;JOIN FETCH&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@MapsId&lt;/code&gt; is cleanest for strict 1:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1 prevention&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;JOIN FETCH&lt;/code&gt; or &lt;code&gt;@EntityGraph&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@NamedSubgraph&lt;/code&gt; for nested graphs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cascade&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CascadeType.ALL&lt;/code&gt; on parent only&lt;/td&gt;
&lt;td&gt;Never cascade from child to parent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orphanRemoval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; for owned children&lt;/td&gt;
&lt;td&gt;Handles disassociation too&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;equals&lt;/code&gt;/&lt;code&gt;hashCode&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Based on business key&lt;/td&gt;
&lt;td&gt;Never based on database ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection for roles&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Set&amp;lt;Role&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safer than &lt;code&gt;List&lt;/code&gt; with multiple joins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best overall mapping&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@MapsId&lt;/code&gt; for strict 1:1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@JoinColumn&lt;/code&gt; for optional or flexible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  12. Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In a &lt;code&gt;@OneToOne&lt;/code&gt; relationship, the &lt;strong&gt;child holds the FK&lt;/strong&gt; (&lt;code&gt;@JoinColumn&lt;/code&gt;); the &lt;strong&gt;parent uses &lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Declaring &lt;code&gt;FetchType.LAZY&lt;/code&gt; on the &lt;strong&gt;inverse side&lt;/strong&gt; does &lt;strong&gt;not&lt;/strong&gt; make it truly lazy — Hibernate fires a probe query anyway because there is no FK column to inspect locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four ways to fix the probe query:&lt;/strong&gt; bytecode instrumentation (&lt;code&gt;@LazyToOne&lt;/code&gt;), &lt;code&gt;JOIN FETCH&lt;/code&gt;, &lt;code&gt;@NamedEntityGraph&lt;/code&gt;, or &lt;code&gt;@MapsId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@MapsId&lt;/code&gt;&lt;/strong&gt; is the cleanest solution for strict one-to-one relationships — the child shares the parent's PK, eliminating the extra FK column and the probe query entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bidirectional = convenience&lt;/strong&gt; (&lt;code&gt;user.getProfile()&lt;/code&gt;) but costs a probe query. &lt;strong&gt;Unidirectional = no probe&lt;/strong&gt; but you lose navigation from the &lt;code&gt;User&lt;/code&gt; side.&lt;/li&gt;
&lt;li&gt;For most applications — skip bytecode plugins, go &lt;strong&gt;unidirectional with &lt;code&gt;@MapsId&lt;/code&gt;&lt;/strong&gt;, and use &lt;code&gt;JOIN FETCH&lt;/code&gt; when you need both entities at once.&lt;/li&gt;
&lt;li&gt;Always use &lt;strong&gt;&lt;code&gt;CascadeType.ALL&lt;/code&gt;&lt;/strong&gt; on the parent side only — never cascade upward from child to parent.&lt;/li&gt;
&lt;li&gt;Base &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; on a &lt;strong&gt;stable business key&lt;/strong&gt; (e.g., &lt;code&gt;username&lt;/code&gt;), never on the auto-generated database ID.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>hibernate</category>
      <category>jpa</category>
      <category>springboot</category>
      <category>database</category>
    </item>
    <item>
      <title>REST API Design: A Comprehensive Guide</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 07 Mar 2026 09:54:56 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/rest-api-design-3am4</link>
      <guid>https://dev.to/ankitdevcode/rest-api-design-3am4</guid>
      <description>&lt;h2&gt;
  
  
  Understanding REST Fundamentals
&lt;/h2&gt;

&lt;p&gt;REST works on top of the HTTP protocol, where each URI represents a resource. Because of this, endpoints should use &lt;strong&gt;nouns, not verbs&lt;/strong&gt;. An RPC-style endpoint like &lt;code&gt;/api/v1/getStudents&lt;/code&gt; becomes simply &lt;code&gt;/api/v1/students&lt;/code&gt; in REST. The distinction between actions is handled by HTTP methods — &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; — rather than the URL itself.&lt;/p&gt;

&lt;p&gt;A REST endpoint is a unique URI that represents a resource. For example, &lt;code&gt;https://demo.app/api/v1/students&lt;/code&gt; is a REST endpoint, where &lt;code&gt;/api/v1/students&lt;/code&gt; is the path and &lt;code&gt;students&lt;/code&gt; is the resource.&lt;/p&gt;

&lt;p&gt;REST does not maintain server-side state — it only transfers state between server and client, which is the origin of the name &lt;em&gt;REpresentational State Transfer&lt;/em&gt;. REST also leverages HTTP cache control, making responses cacheable because every representation is self-descriptive.&lt;/p&gt;

&lt;p&gt;REST operates using three key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resources and URIs&lt;/strong&gt; — the nouns that identify what you're working with&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP methods&lt;/strong&gt; — the verbs that define the action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HATEOAS&lt;/strong&gt; — hypermedia links that guide clients dynamically&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  HTTP Methods
&lt;/h2&gt;

&lt;p&gt;The five primary HTTP methods map to standard CRUD operations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create (or search)&lt;/td&gt;
&lt;td&gt;Use for search when filter params exceed GET limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;Should be side-effect free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full update / replace&lt;/td&gt;
&lt;td&gt;Replaces the entire resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partial update&lt;/td&gt;
&lt;td&gt;Updates only the provided fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;Should be idempotent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Some organisations also expose the &lt;code&gt;HEAD&lt;/code&gt; method to retrieve only response headers without a body — useful for checking resource existence or metadata. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--head&lt;/span&gt; https://api.github.com/users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; REST does not strictly mandate which method maps to which operation, but the conventions above are widely adopted across the industry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  POST
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;POST&lt;/code&gt; is normally used for create operations. There are two notable exceptions where &lt;code&gt;POST&lt;/code&gt; is acceptable for reads:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Long filter criteria&lt;/strong&gt; — &lt;code&gt;GET&lt;/code&gt; query strings are limited to around 2,048 characters. When filter parameters exceed this, &lt;code&gt;POST&lt;/code&gt; is a reasonable alternative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensitive parameters&lt;/strong&gt; — if input parameters contain private data, &lt;code&gt;POST&lt;/code&gt; with HTTPS keeps them out of the URL and server logs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A successful create operation should return &lt;code&gt;201 Created&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  GET
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GET&lt;/code&gt; is used for read operations. It should never modify server state. Successful responses return &lt;code&gt;200 OK&lt;/code&gt; when data is present, or &lt;code&gt;204 No Content&lt;/code&gt; when there is nothing to return.&lt;/p&gt;
&lt;h3&gt;
  
  
  PUT
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PUT&lt;/code&gt; is used for full update or replace operations. It replaces the entire resource with the provided representation. Successful responses return &lt;code&gt;200 OK&lt;/code&gt; with data or &lt;code&gt;204 No Content&lt;/code&gt; without.&lt;/p&gt;
&lt;h3&gt;
  
  
  DELETE
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DELETE&lt;/code&gt; removes a resource. For example, &lt;code&gt;DELETE /licenses/agpl-3.0&lt;/code&gt; deletes the resource identified by the &lt;code&gt;agpl-3.0&lt;/code&gt; key. A successful delete returns &lt;code&gt;204 No Content&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  PATCH
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PATCH&lt;/code&gt; is used for partial updates — only the supplied fields are changed. A successful response returns &lt;code&gt;200 OK&lt;/code&gt;. Unlike &lt;code&gt;PUT&lt;/code&gt;, you do not need to send the full resource representation.&lt;/p&gt;


&lt;h2&gt;
  
  
  HTTP Status Codes
&lt;/h2&gt;

&lt;p&gt;Status codes fall into five categories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100–199&lt;/td&gt;
&lt;td&gt;Informational&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200–299&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300–399&lt;/td&gt;
&lt;td&gt;Redirection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400–499&lt;/td&gt;
&lt;td&gt;Client errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500–599&lt;/td&gt;
&lt;td&gt;Server errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Commonly Used Codes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2xx — Success&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;200 OK&lt;/code&gt; — Request succeeded. Used for &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;PATCH&lt;/code&gt; responses with a body.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;201 Created&lt;/code&gt; — Resource was successfully created.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;202 Accepted&lt;/code&gt; — Request received but processing is deferred (e.g., async or batch jobs).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;204 No Content&lt;/code&gt; — Request succeeded with no response body.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3xx — Redirection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;304 Not Modified&lt;/code&gt; — Resource hasn't changed; client should use its cached copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4xx — Client Errors&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;400 Bad Request&lt;/code&gt; — Missing, malformed, or invalid input parameters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;401 Unauthorized&lt;/code&gt; — Request is unauthenticated (despite the misleading name).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;403 Forbidden&lt;/code&gt; — Authenticated but not authorised to perform the action.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;404 Not Found&lt;/code&gt; — The requested resource does not exist.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;405 Method Not Allowed&lt;/code&gt; — The HTTP method is not supported for this endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;409 Conflict&lt;/code&gt; — Duplicate create or a state conflict (e.g., version mismatch).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429 Too Many Requests&lt;/code&gt; — Rate limit has been exceeded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5xx — Server Errors&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;500 Internal Server Error&lt;/code&gt; — A generic, unexpected server-side failure.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;502 Bad Gateway&lt;/code&gt; — An upstream dependency (e.g., a payment provider) returned an error.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;503 Service Unavailable&lt;/code&gt; — The server is temporarily unable to handle requests (overload or maintenance).&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  HATEOAS
&lt;/h2&gt;

&lt;p&gt;HATEOAS (Hypermedia as the Engine of Application State) means that a REST API dynamically provides links to related actions and resources within its responses — rather than requiring clients to construct URLs themselves.&lt;/p&gt;

&lt;p&gt;A client starts from a single known URL and discovers everything else through the hypermedia links in each response. This has two key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clients don't need hardcoded knowledge of the API's URL structure.&lt;/li&gt;
&lt;li&gt;When endpoint paths change, clients pick up the new URLs automatically through the links.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Use Nouns for Resource Names
&lt;/h3&gt;

&lt;p&gt;HTTP methods already supply the verb. Adding a verb to the URL is redundant and produces RPC-style paths:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# Wrong (RPC style)
GET /getLicenses
POST /createUser

# Correct (REST style)
GET /licenses
POST /users
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Use Plural Names for Collections
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/licenses   ✓
/license    ✗
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A &lt;code&gt;GET /licenses&lt;/code&gt; call returns a collection. Plural naming makes the intent clear and consistent regardless of whether you're fetching one or many.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Version Your APIs
&lt;/h3&gt;

&lt;p&gt;APIs evolve over time, and existing clients may depend on older behaviour. Always include a version identifier so you can introduce breaking changes without affecting existing integrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A — Version in the path (most common):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://demo.app/api/v1/students
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Clients always know exactly which version they're calling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B — Version in the Accept header (GitHub's approach):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Accept: application/vnd.github.v3+json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Allows a default version when no header is present, but requires clients to update the header when upgrading.&lt;/p&gt;

&lt;p&gt;Either approach works — the important thing is to pick one and apply it consistently from day one.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Model Nested Resources
&lt;/h3&gt;

&lt;p&gt;When a resource belongs to another, reflect that in the URL hierarchy:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET    /customers/1/addresses       # all addresses for customer 1
GET    /customers/1/addresses/2     # address 2 of customer 1
POST   /customers/1/addresses       # add a new address
PUT    /customers/1/addresses/2     # replace address 2
PATCH  /customers/1/addresses/2     # partially update address 2
DELETE /customers/1/addresses/2     # delete address 2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For resources that exist independently (e.g., payments in a microservices architecture), a top-level endpoint is often cleaner:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /payments/1    # instead of /orders/1/payments/1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;HATEOAS helps here — the order response can include a payment link rather than the client needing to know the URL structure upfront.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Secure Your APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Always use &lt;strong&gt;HTTPS&lt;/strong&gt; — never expose REST APIs over plain HTTP.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;JWT or OAuth 2.0&lt;/strong&gt; tokens for authentication. REST is stateless, so cookies and server-side sessions are not appropriate.&lt;/li&gt;
&lt;li&gt;Regularly review &lt;a href="https://owasp.org/www-project-api-security/" rel="noopener noreferrer"&gt;OWASP's API Security Top 10&lt;/a&gt; for current threats and mitigations.&lt;/li&gt;
&lt;li&gt;Validate all inputs on the server side regardless of client-side validation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  6. Implement Caching
&lt;/h3&gt;

&lt;p&gt;HTTP provides two standard mechanisms for cache validation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ETag&lt;/strong&gt; — the server returns a hash of the response body in the &lt;code&gt;ETag&lt;/code&gt; header. The client sends it back as &lt;code&gt;If-None-Match&lt;/code&gt; on subsequent requests. If the resource hasn't changed, the server returns &lt;code&gt;304 Not Modified&lt;/code&gt; and saves the bandwidth of resending the body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last-Modified&lt;/strong&gt; — the server returns the resource's last modification timestamp. The client sends it back as &lt;code&gt;If-Modified-Since&lt;/code&gt;. The server returns &lt;code&gt;304&lt;/code&gt; if nothing has changed since that timestamp. This is less precise than ETag and should be used as a fallback.&lt;/p&gt;
&lt;h3&gt;
  
  
  7. Enforce Rate Limiting
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;429 Too Many Requests&lt;/code&gt; when a client exceeds its allowed request quota. Communicate rate limit status via response headers so clients can adapt:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Ratelimit-Limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests allowed per period&lt;/td&gt;
&lt;td&gt;&lt;code&gt;60&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Ratelimit-Remaining&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests left in current period&lt;/td&gt;
&lt;td&gt;&lt;code&gt;55&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Ratelimit-Used&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests used in current period&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Ratelimit-Reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Seconds until the period resets&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1601299930&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  8. Support Filtering, Sorting, and Pagination
&lt;/h3&gt;

&lt;p&gt;For endpoints that return collections, always support query parameters to avoid returning unbounded result sets:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /products?category=electronics&amp;amp;sort=price       # filter + sort
GET /users?page=1&amp;amp;size=20                           # pagination
GET /orders?status=pending&amp;amp;sort=created_at&amp;amp;page=2  # combined
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  9. Maintain Documentation
&lt;/h3&gt;

&lt;p&gt;Good documentation is part of your API contract. It should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stay in sync with the current implementation, including version history.&lt;/li&gt;
&lt;li&gt;Include code samples and example request/response pairs.&lt;/li&gt;
&lt;li&gt;Clearly document deprecated endpoints and provide migration paths.&lt;/li&gt;
&lt;li&gt;Be generated from the code where possible (e.g., OpenAPI/Swagger) to reduce drift.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  10. Return Consistent Error Responses
&lt;/h3&gt;

&lt;p&gt;Error responses should follow a predictable structure so clients can handle them programmatically:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bad Request"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The 'email' field is required."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/v1/users"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Consistency here dramatically reduces the integration effort for API consumers.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL naming&lt;/td&gt;
&lt;td&gt;Nouns, plural, lowercase, hyphen-separated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP methods&lt;/td&gt;
&lt;td&gt;Match semantics: &lt;code&gt;GET&lt;/code&gt; reads, &lt;code&gt;POST&lt;/code&gt; creates, &lt;code&gt;PUT&lt;/code&gt; replaces, &lt;code&gt;PATCH&lt;/code&gt; partially updates, &lt;code&gt;DELETE&lt;/code&gt; removes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status codes&lt;/td&gt;
&lt;td&gt;Use the most specific code; avoid overusing &lt;code&gt;200&lt;/code&gt; for everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versioning&lt;/td&gt;
&lt;td&gt;Include from day one (&lt;code&gt;/api/v1/&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;JWT or OAuth 2.0 — no cookies or sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;ETag&lt;/code&gt; or &lt;code&gt;Last-Modified&lt;/code&gt; headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limiting&lt;/td&gt;
&lt;td&gt;Return &lt;code&gt;429&lt;/code&gt; with &lt;code&gt;X-Ratelimit-*&lt;/code&gt; headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;?page=&amp;amp;size=&lt;/code&gt; query params on collection endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;OpenAPI/Swagger, kept current, with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error format&lt;/td&gt;
&lt;td&gt;Consistent JSON structure across all endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI tools such as ChatGpt and Claude&lt;/li&gt;
&lt;li&gt;

&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.packtpub.com/en-us/product/modern-api-development-with-spring-and-spring-boot-9781800562479" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbookimages.packtpub.com%2Fproduct-images%2FB16561%2Fpage_453.jpg" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.packtpub.com/en-us/product/modern-api-development-with-spring-and-spring-boot-9781800562479" rel="noopener noreferrer" class="c-link"&gt;
            Modern API Development with Spring and Spring Boot | Web Development | Paperback
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Design highly scalable and maintainable APIs with REST, gRPC, GraphQL, and the reactive paradigm. 12 customer reviews. Top rated Web Development products.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.packtpub.com%2Ffavicon.ico"&gt;
          packtpub.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>restapi</category>
      <category>api</category>
      <category>java</category>
      <category>design</category>
    </item>
    <item>
      <title>Java Virtual Threads — Quick Guide</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sun, 01 Feb 2026 12:12:55 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/java-virtual-threads-quick-guide-1jca</link>
      <guid>https://dev.to/ankitdevcode/java-virtual-threads-quick-guide-1jca</guid>
      <description>&lt;h2&gt;
  
  
  Java Virtual Threads — Quick Guide
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Java 21+ · Spring Boot 3.2+ · Project Loom&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A concise, production-focused guide to Java &lt;strong&gt;Virtual Threads&lt;/strong&gt; — what they are, how to enable them, when to use them, and the real-world pitfalls that can silently hurt performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  01 · What Are Virtual Threads
&lt;/h2&gt;

&lt;p&gt;Before Project Loom, there is only one type of threads in Java, which is called platform thread in Project Loom. Platform threads are typically mapped 1:1 to kernel threads scheduled by the operating system. In Project Loom, virtual threads are introduced as a new type of threads.&lt;/p&gt;

&lt;p&gt;Virtual threads are typically user-mode threads scheduled by the Java runtime rather than the operating system. Virtual threads are mapped M:N to kernel threads.&lt;/p&gt;

&lt;p&gt;Platform and virtual threads are both represented using java.lang.Thread&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely lightweight compared to platform (OS) threads.&lt;/li&gt;
&lt;li&gt;Millions of virtual threads can be created safely.&lt;/li&gt;
&lt;li&gt;Allow developers to write simple, blocking-style code while remaining highly scalable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to create virtual threads?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first approach to create virtual threads is using the&amp;nbsp;&lt;code&gt;Thread.ofVirtual&lt;/code&gt;&amp;nbsp;method.&lt;/p&gt;

&lt;p&gt;In the code below, a new virtual thread is created and started. The return value is an instance of&amp;nbsp;&lt;code&gt;java.lang.Thread&lt;/code&gt;&amp;nbsp;object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var thread = Thread.ofVirtual().name("My virtual thread")
    .start(() -&amp;gt; System.out.println("I'm running"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second approach is using&amp;nbsp;&lt;code&gt;Thread.startVirtualThread(Runnable task)&lt;/code&gt;&amp;nbsp;method. This is the same as calling&amp;nbsp;&lt;code&gt;Thread.ofVirtual().start(task)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The third approach is using&amp;nbsp;&lt;code&gt;ThreadFactory&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var factory = Thread.ofVirtual().factory();
var thread = factory.newThread(() -&amp;gt; System.out.println("Create in factory"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to check if a thread is virtual?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The new&amp;nbsp;&lt;code&gt;isVirtual()&lt;/code&gt;&amp;nbsp;method in&amp;nbsp;&lt;code&gt;java.lang.Thread&lt;/code&gt;&amp;nbsp;returns&amp;nbsp;&lt;code&gt;true&lt;/code&gt;&amp;nbsp;is this thread is a virtual thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can virtual threads be non-daemon threads?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Virtual threads are always daemon threads. So they cannot prevent JVM from terminating. Calling&amp;nbsp;&lt;code&gt;setDaemon(false)&lt;/code&gt;&amp;nbsp;on a virtual thread will throw an&amp;nbsp;&lt;code&gt;IllegalArgumentException&lt;/code&gt;&amp;nbsp;exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should virtual threads be pooled?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Virtual threads are light-weight. There is no need to pool them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can virtual threads support thread-local variables?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Virtual threads support both thread-local variables (&lt;code&gt;ThreadLocal&lt;/code&gt;) and inheritable thread-local variables (&lt;code&gt;InheritableThreadLocal&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can virtual threads support thread-local variables?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Virtual threads support both thread-local variables (&lt;code&gt;ThreadLocal&lt;/code&gt;) and inheritable thread-local variables (&lt;code&gt;InheritableThreadLocal&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can&amp;nbsp;&lt;code&gt;ExecutorService&lt;/code&gt;&amp;nbsp;use virtual threads?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An&amp;nbsp;&lt;code&gt;ExecutorService&lt;/code&gt;&amp;nbsp;can start a virtual thread for each task. This kind of&amp;nbsp;&lt;code&gt;ExecutorService&lt;/code&gt;s can be created using&amp;nbsp;&lt;code&gt;Executors.newVirtualThreadPerTaskExecutor()&lt;/code&gt;&amp;nbsp;or&amp;nbsp;&lt;code&gt;Executors.newThreadPerTaskExecutor(ThreadFactory threadFactory)&lt;/code&gt;&amp;nbsp;methods. The number of virtual threads created by the&amp;nbsp;&lt;code&gt;Executor&lt;/code&gt;&amp;nbsp;is unbounded.&lt;/p&gt;

&lt;p&gt;In the code below, a new&amp;nbsp;&lt;code&gt;ExecutorService&lt;/code&gt;&amp;nbsp;is created to use virtual threads. 10000 tasks are submitted to this&amp;nbsp;&lt;code&gt;ExecutorService&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
  IntStream.range(0, 10_00).forEach(i -&amp;gt; executor.submit(() -&amp;gt; {
    Thread.sleep(Duration.ofSeconds(1));
    return i;
  }));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blocking comparison
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;OS Thread blocks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RestTemplate&lt;/code&gt; blocks an OS thread&lt;/li&gt;
&lt;li&gt;Thread is idle during I/O&lt;/li&gt;
&lt;li&gt;Under load → thread exhaustion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Virtual Thread blocks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JVM suspends the virtual thread&lt;/li&gt;
&lt;li&gt;Carrier thread is released immediately&lt;/li&gt;
&lt;li&gt;Scales safely under high concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/ankitdevcode/concurrency-and-asynchronous-programming-part-1-2jah"&gt;Why we need virtual threads?&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Enable in Spring Boot
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;One property. No code changes.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application.yml&lt;/span&gt;
&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;servlet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;threads&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;virtual-threads-enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java 21+&lt;/strong&gt; (final, not preview)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spring Boot 3.2+&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each HTTP request runs on a fresh virtual thread&lt;/li&gt;
&lt;li&gt;Controllers, services, &lt;code&gt;RestTemplate&lt;/code&gt; → unchanged&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What &lt;code&gt;virtual-threads-enabled: true&lt;/code&gt; Actually Does in Spring
&lt;/h2&gt;

&lt;p&gt;It replaces Tomcat’s entire servlet thread pool with a &lt;strong&gt;virtual-thread-per-request executor&lt;/strong&gt;. Every incoming HTTP request is immediately assigned a new virtual thread. There is no fixed pool size — Tomcat doesn’t cap anything. The JVM manages it all.&lt;/p&gt;

&lt;p&gt;This means your entire request lifecycle — from the moment the request hits the &lt;code&gt;DispatcherServlet&lt;/code&gt; to the moment the response is written — runs on a virtual thread. Every blocking call inside that chain (&lt;code&gt;RestTemplate&lt;/code&gt;, JDBC, &lt;code&gt;Thread.sleep()&lt;/code&gt;) is automatically cheap because it’s already on a virtual thread.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Manual Offload Approach
&lt;/h2&gt;

&lt;p&gt;Tomcat’s default OS thread pool handles accept and dispatch, then the work is explicitly handed off to a virtual thread executor. For example, &lt;code&gt;CompletableFuture.supplyAsync()&lt;/code&gt; offloads work to the virtual thread executor. The OS thread that accepted the request is released immediately.&lt;/p&gt;

&lt;p&gt;Spring MVC knows how to handle a returned &lt;code&gt;CompletableFuture&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It suspends servlet processing&lt;/li&gt;
&lt;li&gt;It resumes when the future completes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key point: Spring MVC &lt;strong&gt;does not block&lt;/strong&gt; the servlet thread waiting for the future. It registers a callback internally and frees the thread immediately.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Service and Client Layers Remain Unchanged&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The service and client layers stay completely unchanged in both approaches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Virtual Threads Adoption Strategy in Spring Boot
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;The existing Spring Boot microservice handles incoming HTTP requests using Spring MVC (Servlet stack) and communicates with multiple downstream services using blocking clients such as &lt;code&gt;RestTemplate&lt;/code&gt; and JDBC.&lt;/p&gt;

&lt;p&gt;Key constraints and characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The current implementation &lt;strong&gt;cannot be changed or rewritten&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The codebase contains:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;synchronized&lt;/code&gt; blocks and methods&lt;/li&gt;
&lt;li&gt;Heavy reliance on &lt;code&gt;ThreadLocal&lt;/code&gt; (SecurityContext, MDC, request attributes)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The service performs &lt;strong&gt;I/O-heavy aggregation&lt;/strong&gt; across multiple downstream services.&lt;/li&gt;

&lt;li&gt;Scalability issues arise due to thread blocking under load.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The goal is to improve concurrency and scalability using &lt;strong&gt;Java Virtual Threads&lt;/strong&gt;, without breaking existing behavior or introducing subtle runtime risks.&lt;/p&gt;

&lt;p&gt;Two approaches are available in Spring Boot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Property-based virtual threads (&lt;code&gt;spring.threads.virtual.enabled=true&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Manual offloading to a virtual-thread executor using &lt;code&gt;CompletableFuture&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Option 1: Property-Based Virtual Threads (Global Enablement)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;Enabling:&lt;/p&gt;

&lt;p&gt;(&lt;code&gt;spring.threads.virtual.enabled=true&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;replaces Tomcat's servlet thread pool with a &lt;strong&gt;virtual-thread-per-request&lt;/strong&gt; executor.&lt;/p&gt;

&lt;p&gt;Each HTTP request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is assigned a fresh virtual thread&lt;/li&gt;
&lt;li&gt;Runs entirely on that virtual thread (filters → controllers → services → response)&lt;/li&gt;
&lt;li&gt;Executes blocking calls cheaply (RestTemplate, JDBC, Thread.sleep)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Zero code changes&lt;/li&gt;
&lt;li&gt;Uniform behavior across the entire application&lt;/li&gt;
&lt;li&gt;Automatic scalability for blocking I/O&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Risks and Limitations
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Pinning Risk
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;synchronized&lt;/code&gt; blocks &lt;strong&gt;pin carrier threads&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Pinning is invisible and global&lt;/li&gt;
&lt;li&gt;Concurrent access can exhaust the small carrier thread pool
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Virtual Thread]

↓

synchronized block  ← carrier pinned

↓

blocking I/O
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If N concurrent requests enter pinned sections, N carrier threads are required. With only ~CPU-count carriers available, the application can stall.&lt;/p&gt;

&lt;h3&gt;
  
  
  ThreadLocal Assumptions Break
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads are short-lived&lt;/li&gt;
&lt;li&gt;No thread reuse across requests&lt;/li&gt;
&lt;li&gt;ThreadLocal data does not persist beyond a single request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This breaks assumptions made by existing code that was written for pooled OS threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ThreadLocal Abuse:&lt;/strong&gt; If your project stores massive objects in ThreadLocal, you might run into memory issues because you could suddenly have 100,000 threads instead of 200.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of Control
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No isolation boundary&lt;/li&gt;
&lt;li&gt;No way to selectively exclude endpoints or code paths&lt;/li&gt;
&lt;li&gt;Fixing issues requires rewriting synchronized and ThreadLocal-dependent code&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Option 2: Manual Offload to Virtual Threads (Selective Adoption)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tomcat continues to use its default &lt;strong&gt;OS-thread servlet pool&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Existing code runs unchanged on OS threads&lt;/li&gt;
&lt;li&gt;I/O-heavy logic is explicitly offloaded using:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CompletableFuture.supplyAsync(task, virtualThreadExecutor)&lt;/p&gt;

&lt;p&gt;Spring MVC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Natively supports &lt;code&gt;CompletableFuture&lt;/code&gt; return types&lt;/li&gt;
&lt;li&gt;Suspends request processing&lt;/li&gt;
&lt;li&gt;Releases the servlet thread immediately&lt;/li&gt;
&lt;li&gt;Resumes when the future completes&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ThreadLocal Considerations
&lt;/h3&gt;

&lt;p&gt;Offloading creates a &lt;strong&gt;hard thread boundary&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Context such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SecurityContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MDC tracing data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;is &lt;strong&gt;not propagated automatically&lt;/strong&gt; and must be captured and restored manually.&lt;/p&gt;

&lt;p&gt;This boundary is explicit and controlled.&lt;/p&gt;




&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Preserves existing assumptions (&lt;code&gt;synchronized&lt;/code&gt;, ThreadLocal)&lt;/li&gt;
&lt;li&gt;Avoids carrier-thread pinning in legacy code&lt;/li&gt;
&lt;li&gt;Allows targeted use of virtual threads only where beneficial&lt;/li&gt;
&lt;li&gt;Enables incremental migration&lt;/li&gt;
&lt;li&gt;Clear isolation between OS-thread and virtual-thread execution&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Slightly more boilerplate&lt;/li&gt;
&lt;li&gt;Requires explicit context propagation&lt;/li&gt;
&lt;li&gt;Virtual thread usage must be consciously applied&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Consequences
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Positive
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Improved scalability for I/O-heavy endpoints&lt;/li&gt;
&lt;li&gt;No need to refactor existing synchronized code&lt;/li&gt;
&lt;li&gt;Predictable runtime behavior&lt;/li&gt;
&lt;li&gt;Clear migration path&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Negative
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Additional boilerplate for context propagation&lt;/li&gt;
&lt;li&gt;Requires discipline to maintain offload boundaries&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;The property-based virtual thread approach is suitable only for codebases that are already virtual-thread-friendly.&lt;/p&gt;

&lt;p&gt;For this system, &lt;strong&gt;manual offloading is the safest and most effective strategy&lt;/strong&gt;, delivering the benefits of virtual threads while preserving correctness and operational stability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pitfalls (Read Before Production)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;synchronized&lt;/code&gt; pins carrier threads
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual thread becomes glued to the carrier&lt;/li&gt;
&lt;li&gt;9 concurrent requests + 8 carriers → deadlock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix: use &lt;code&gt;ReentrantLock&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pins carrier&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/p/{id}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Safe&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/p/{id}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ThreadLocal context loss during manual offload
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What breaks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDC&lt;/li&gt;
&lt;li&gt;SecurityContext&lt;/li&gt;
&lt;li&gt;RequestAttributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix: capture &amp;amp; restore context&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mdc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCopyOfContextMap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;SecurityContext&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mdc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="no"&gt;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContextMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mdc&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sec&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doWork&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clearContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ioExecutor&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 This issue &lt;strong&gt;does not exist&lt;/strong&gt; when using &lt;code&gt;virtual-threads-enabled: true&lt;/code&gt; globally.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  ThreadLocal leaks with pooled executors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Someone replaces the executor with a fixed pool&lt;/li&gt;
&lt;li&gt;ThreadLocals leak across requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix: enforce correct executor&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Executor&lt;/span&gt; &lt;span class="nf"&gt;ioExecutor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Native (JNI) calls pin silently
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some JDBC drivers&lt;/li&gt;
&lt;li&gt;Crypto libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contain the damage&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="no"&gt;NATIVE_POOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callNative&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;NATIVE_POOL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nativeLib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable pin logging (dev only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-XX:+PrintVirtualThreadPinning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  MVC + WebFlux together
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If both starters are present, Spring chooses &lt;strong&gt;MVC&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No warning is shown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual Threads → keep &lt;code&gt;spring-boot-starter-web&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;starter-webflux&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  CPU-bound work on virtual threads
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Anti-pattern&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heavy computation&lt;/li&gt;
&lt;li&gt;Image processing&lt;/li&gt;
&lt;li&gt;Crypto loops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Correct split&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// I/O work&lt;/span&gt;
&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(...),&lt;/span&gt;
    &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// CPU work&lt;/span&gt;
&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;heavyComputation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;ForkJoinPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commonPool&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Virtual Threads are the best choice for blocking, I/O-heavy Spring Boot services you cannot rewrite.&lt;/p&gt;

&lt;p&gt;They give you scalability, simplicity, and production safety — without reactive complexity.&lt;/p&gt;

</description>
      <category>virtualthread</category>
      <category>java21</category>
      <category>concurrency</category>
      <category>programming</category>
    </item>
    <item>
      <title>Concurrency and Asynchronous Programming: Introduction</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Fri, 30 Jan 2026 17:24:29 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/concurrency-and-asynchronous-programming-part-1-2jah</link>
      <guid>https://dev.to/ankitdevcode/concurrency-and-asynchronous-programming-part-1-2jah</guid>
      <description>&lt;h2&gt;
  
  
  What is Parallel Programming?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tasks &lt;strong&gt;run at the same time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Progress happens simultaneously at any given instant.&lt;/li&gt;
&lt;li&gt;Requires sufficient hardware resources (e.g., multiple CPU cores).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Walking and talking:

&lt;ul&gt;
&lt;li&gt;Both actions occur at the same time.&lt;/li&gt;
&lt;li&gt;At every moment, both are progressing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Concurrent Programming?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tasks &lt;strong&gt;make progress over time&lt;/strong&gt;, but not at the same instant.&lt;/li&gt;
&lt;li&gt;The system switches between tasks.&lt;/li&gt;
&lt;li&gt;Over a time interval, all tasks move forward.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Talking and drinking:

&lt;ul&gt;
&lt;li&gt;You alternate between the two.&lt;/li&gt;
&lt;li&gt;At any moment, you’re doing one or the other, not both.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What “Asynchronous” Really Means?
&lt;/h2&gt;

&lt;p&gt;Tasks &lt;strong&gt;don’t block&lt;/strong&gt; while waiting for something (I/O, timers, network).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Asynchrony is about &lt;strong&gt;waiting efficiently&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A task starts work&lt;/li&gt;
&lt;li&gt;It pauses when waiting&lt;/li&gt;
&lt;li&gt;Resumes later via callbacks, promises&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Misconception
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous ≠ no waiting&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Tasks still wait for data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Real Question
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the thread block while waiting?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocking threads = poor scalability.&lt;/li&gt;
&lt;li&gt;Free threads = better resource utilization.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The twist: they’re not opposites
&lt;/h2&gt;

&lt;p&gt;You can have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent + synchronous&lt;/strong&gt; (multiple threads blocking)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-threaded + asynchronous&lt;/strong&gt; (Node.js style)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent + asynchronous&lt;/strong&gt; (modern web servers)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel&lt;/strong&gt; (true simultaneous execution on multiple cores)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Parallelism&lt;/strong&gt; = literally running at the same time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency&lt;/strong&gt; = dealing with multiple things&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asynchrony&lt;/strong&gt; = not blocking while waiting&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world analogy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency:&lt;/strong&gt; A chef cooking 3 dishes at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous:&lt;/strong&gt; Putting something in the oven and doing other prep instead of staring at it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel:&lt;/strong&gt; Multiple chefs cooking at the same time&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why threads are expensive?
&lt;/h2&gt;

&lt;p&gt;Threads are considered &lt;strong&gt;expensive&lt;/strong&gt; because each thread consumes &lt;strong&gt;significant system resources&lt;/strong&gt;, even when it’s idle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main reasons:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memory usage (stack space)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Each thread has its own &lt;strong&gt;stack&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Typical stack size:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~1 MB per thread&lt;/strong&gt; (common default on 64-bit systems)&lt;/li&gt;
&lt;li&gt;Can range from &lt;strong&gt;256 KB to several MB&lt;/strong&gt;, depending on OS and JVM/runtime configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1,000 threads ≈ &lt;strong&gt;~1 GB of memory&lt;/strong&gt; just for stacks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context switching overhead&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The CPU must save and restore thread state when switching&lt;/li&gt;
&lt;li&gt;Frequent context switches reduce CPU efficiency&lt;/li&gt;
&lt;li&gt;Becomes costly at high thread counts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling overhead&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The OS scheduler must manage runnable threads&lt;/li&gt;
&lt;li&gt;Large numbers of threads increase scheduling complexity and latency&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synchronization costs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Threads often require locks, mutexes, or other coordination mechanisms&lt;/li&gt;
&lt;li&gt;Leads to contention, deadlocks, and performance bottlenecks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why Blocking Threads Hurt Scalability
&lt;/h2&gt;

&lt;p&gt;When threads block on I/O:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They sit idle.&lt;/li&gt;
&lt;li&gt;More threads are created to compensate.&lt;/li&gt;
&lt;li&gt;Threads are limited by:

&lt;ul&gt;
&lt;li&gt;CPU cores.&lt;/li&gt;
&lt;li&gt;Memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More machines.&lt;/li&gt;
&lt;li&gt;More architectural complexity.&lt;/li&gt;
&lt;li&gt;Higher costs (and environmental impact).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Evolution of Java Multithreading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Java 5 – &lt;code&gt;ExecutorService&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduced thread pools.&lt;/li&gt;
&lt;li&gt;Solved:

&lt;ul&gt;
&lt;li&gt;Uncontrolled thread creation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;New problem:

&lt;ul&gt;
&lt;li&gt;Thread-pool–induced deadlocks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 7 – Fork/Join Framework
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduced &lt;strong&gt;work stealing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Reduced pool starvation issues.&lt;/li&gt;
&lt;li&gt;Well-suited for &lt;strong&gt;CPU-bound parallelism&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 8 – Expressive Concurrency
&lt;/h3&gt;

&lt;p&gt;Introduced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel Streams&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CompletableFuture&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enabled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More declarative parallelism.&lt;/li&gt;
&lt;li&gt;Better asynchronous composition.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 20 / 21 – Virtual Threads
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Another major step forward.&lt;/li&gt;
&lt;li&gt;Makes blocking cheap and scalable.&lt;/li&gt;
&lt;li&gt;Simplifies concurrency for many workloads.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Are Virtual Threads?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads are a &lt;strong&gt;lightweight threading model&lt;/strong&gt; introduced with &lt;strong&gt;Project Loom&lt;/strong&gt; in Java.&lt;/li&gt;
&lt;li&gt;They are managed by the &lt;strong&gt;JVM&lt;/strong&gt;, not the operating system.&lt;/li&gt;
&lt;li&gt;Designed to support &lt;strong&gt;massive concurrency&lt;/strong&gt; with minimal resource usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Characteristics&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely lightweight compared to platform (OS) threads.&lt;/li&gt;
&lt;li&gt;Millions of virtual threads can be created safely.&lt;/li&gt;
&lt;li&gt;Allow developers to write &lt;strong&gt;simple, blocking-style code&lt;/strong&gt; while remaining highly scalable.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem Virtual Threads Aim to Solve
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Traditional Java Concurrency Issues
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Thread-per-request model:

&lt;ul&gt;
&lt;li&gt;Threads block during I/O (DB, network, file).&lt;/li&gt;
&lt;li&gt;Blocking threads consume memory and OS resources.&lt;/li&gt;
&lt;li&gt;Scalability is limited by thread count.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;To scale:

&lt;ul&gt;
&lt;li&gt;More threads → more memory.&lt;/li&gt;
&lt;li&gt;More machines → higher cost and complexity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rise of Reactive Programming
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reactive frameworks (Reactor, RxJava) emerged to:

&lt;ul&gt;
&lt;li&gt;Avoid blocking OS threads.&lt;/li&gt;
&lt;li&gt;Handle high concurrency using non-blocking I/O.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Downsides:

&lt;ul&gt;
&lt;li&gt;Complex APIs.&lt;/li&gt;
&lt;li&gt;Harder to read, debug, and reason about.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Virtual Threads Work?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads are &lt;strong&gt;scheduled by the JVM&lt;/strong&gt;, not the OS.&lt;/li&gt;
&lt;li&gt;When a virtual thread blocks on I/O:

&lt;ul&gt;
&lt;li&gt;It is &lt;strong&gt;unmounted&lt;/strong&gt; from its carrier (platform) thread.&lt;/li&gt;
&lt;li&gt;The carrier thread is reused for other work.&lt;/li&gt;
&lt;li&gt;The virtual thread is &lt;strong&gt;remounted&lt;/strong&gt; once I/O completes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Result:

&lt;ul&gt;
&lt;li&gt;Blocking no longer wastes threads.&lt;/li&gt;
&lt;li&gt;High scalability with familiar programming models.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Virtual Threads vs Reactive Programming
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shared Goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both aim to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maximize concurrency.&lt;/li&gt;
&lt;li&gt;Avoid wasting threads during I/O.&lt;/li&gt;
&lt;li&gt;Improve scalability of server-side applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Differences&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Virtual Threads&lt;/th&gt;
&lt;th&gt;Reactive Programming&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blocking code is acceptable&lt;/td&gt;
&lt;td&gt;Requires non-blocking code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple, imperative style&lt;/td&gt;
&lt;td&gt;Functional, stream-based style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Easier to read and debug&lt;/td&gt;
&lt;td&gt;Steeper learning curve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works with existing APIs&lt;/td&gt;
&lt;td&gt;Requires reactive-compatible APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Core Argument
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads make &lt;strong&gt;thread-per-task&lt;/strong&gt; scalable again.&lt;/li&gt;
&lt;li&gt;This reduces the need for reactive frameworks in many common cases.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Benefits of Virtual Threads
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write synchronous, blocking code.&lt;/li&gt;
&lt;li&gt;No need to manage callbacks or reactive pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocking no longer ties up OS threads.&lt;/li&gt;
&lt;li&gt;Supports very high concurrency with low overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Compatibility&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works with existing Java libraries and APIs.&lt;/li&gt;
&lt;li&gt;No need to rewrite large codebases.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Does Virtual Threads Kill Reactive Programming?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Short Answer:&lt;/strong&gt; No — But It Shrinks Its Use Cases&lt;/p&gt;

&lt;p&gt;Reactive programming still matters when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-grained event streams are required.&lt;/li&gt;
&lt;li&gt;Backpressure control is critical.&lt;/li&gt;
&lt;li&gt;Complex data-flow transformations are needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Virtual threads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do &lt;strong&gt;not&lt;/strong&gt; automatically provide:

&lt;ul&gt;
&lt;li&gt;Backpressure.&lt;/li&gt;
&lt;li&gt;Stream composition.&lt;/li&gt;
&lt;li&gt;Reactive operators.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Why Reactive Programming Existed&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To avoid blocking OS threads and improve scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What Changed&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads make blocking cheap and scalable.&lt;/li&gt;
&lt;li&gt;Structured concurrency brings better control and safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practical Impact&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many server-side workloads can return to:

&lt;ul&gt;
&lt;li&gt;Simple, imperative code.&lt;/li&gt;
&lt;li&gt;Thread-per-request style.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reactive programming remains relevant for specialized scenarios, but is no longer mandatory for scalability.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>asynchronous</category>
      <category>virtualthread</category>
      <category>reactive</category>
      <category>concurrency</category>
    </item>
  </channel>
</rss>
