Java Internals & Concurrency/Module 01 — Concurrency cơ bản: tổng quan
1/75
Bài 1 / 75~10 phútConcurrency cơ bảnMiễn phí lượt xem

Module 01 — Concurrency cơ bản: tổng quan

Bản đồ module concurrency: từ process/thread và race condition, qua bộ công cụ java.util.concurrent, tới executor, virtual thread và structured concurrency.

TL;DR: Module này dạy cách viết code Java đúng khi nhiều thread cùng chạm vào dữ liệu. 35 bài (22 chủ đề đánh số — chủ đề dài được tách thành cặp bài NN/NNb cho vừa một lần đọc) chia ba phần: nền tảng (vì sao race condition xảy ra, hai vấn đề atomicity và visibility, và bốn chiến lược thread safety), công cụ (atomic, lock, concurrent collection, blocking queue, synchronizer), và kiến trúc (executor, CompletableFuture, fork/join, virtual thread, structured concurrency). Một capstone duy nhất — hệ thống đặt vé TicketFlow — tiến hóa từ v0 đầy race condition tới v4 chạy trên virtual thread, để mỗi công cụ mới đều được gắn vào đúng một vấn đề thật.

Vì sao module này tồn tại

Hãy hình dung một tình huống mà gần như mọi team backend đều từng gặp một biến thể của nó. TicketFlow — hệ thống đặt vé sự kiện — mở bán 500 vé cho một đêm nhạc. Code đặt vé rất hợp lý: đọc số vé đã bán, kiểm tra còn chỗ, ghi tăng một. Test pass, staging chạy mượt. Đêm mở bán, 2.000 người bấm nút cùng lúc — và sáng hôm sau, database ghi nhận 503 vé đã bán cho 500 ghế. Ba khách hàng cầm vé hợp lệ cho ghế không tồn tại.

Không một dòng nào trong code đó sai khi chạy đơn luồng. Nó chỉ sai khi hai thread cùng đọc thấy "còn một chỗ" rồi cùng bán. Loại bug này không xuất hiện trong unit test, không tái hiện được trên máy dev, chỉ bùng ra dưới tải thật — và khi đã ra production thì chi phí không đo bằng giờ debug mà bằng tiền hoàn vé và niềm tin của khách.

Concurrency là kỹ năng phân tầng dev Java rõ nhất: ai cũng viết được code chạy được trên nhiều thread, nhưng viết code chạy đúng đòi hỏi hiểu cơ chế — memory model, happens-before, compound action, contention. Module này xây từng viên gạch của phần hiểu đó, và dùng chính bug oversell ở trên làm sợi chỉ xuyên suốt: bạn sẽ gặp lại nó ở bài thread safety dưới tên BookingService v0, rồi tự tay vá nó bằng từng công cụ học được.

Sau module này bạn sẽ

  • Giải thích được vì sao concurrency bug xảy ra: shared mutable state, hai vấn đề độc lập atomicity và visibility, và quan hệ happens-before của Java Memory Model.
  • Chẩn đoán được race condition trong code thật bằng cách trace các interleaving xui xẻo nhất và chỉ ra compound action không nguyên tử.
  • Triển khai được class thread-safe theo bốn chiến lược: confinement, immutability, synchronization, và delegation cho concurrent collection.
  • Chọn đúng cơ chế đồng bộ — volatile, synchronized, Atomic/LongAdder, ReentrantLock, ReadWriteLock/StampedLock hay ConcurrentHashMap — theo pattern truy cập và mức contention.
  • Thiết kế được kiến trúc thực thi tách task khỏi thread: producer–consumer với BlockingQueue, thread pool tuning có backpressure, và pipeline async bằng CompletableFuture.
  • Trace được vòng đời một request trên virtual thread — mount/unmount, pinning — và quyết định khi nào di cư từ thread pool sang virtual thread cùng structured concurrency.

Lộ trình module

Ba mươi lăm bài xếp thành ba phần theo 22 chủ đề đánh số — mỗi chủ đề dài tách thành cặp NN/NNb (ví dụ 08 dạy ngữ nghĩa volatile/synchronized, 08b mổ bên trong khóa) — và thứ tự của chúng không ngẫu nhiên.

Phần 1 — Nền tảng (bài 01–09): trước khi cầm bất kỳ công cụ nào, bạn cần hiểu vấn đề. Bài 01–02 dựng khung từ hệ điều hành lên: process, thread, vòng đời và cách hủy một thread tử tế. Bài 03 đặt viên đá trung tâm của cả module — shared mutable state — và chỉ ra rằng nó sinh ra hai vấn đề độc lập; đây cũng là nơi TicketFlow v0 xuất hiện với đúng bug oversell ở trên. Bài 04 mổ vấn đề thứ nhất (atomicity: compound action bị xen ngang), bài 05 mổ vấn đề thứ hai (visibility: thread không thấy thay đổi của nhau). Bài 06–07 dạy hai cách cả hai vấn đề: không chia sẻ (confinement) và không thay đổi (immutability). Chỉ khi cả hai đường né đều không đi được, bài 08 mới rút công cụ đồng bộ đầu tiên: volatilesynchronized — TicketFlow lên v1, vá race bằng Monitor Pattern. Và vì synchronized còn một nửa nữa — không chỉ loại trừ mà còn phối hợp giữa các thread — bài 09 khép phần nền tảng với wait/notify và guarded block theo Java Monitor Pattern.

Phần 2 — Công cụ (bài 10–16): synchronized đúng nhưng thô. Phần này mở dần hộp đồ nghề của java.util.concurrent theo trục từ thấp lên cao: CAS và Atomic (10) cho thao tác một biến không cần khóa; explicit lock (11) rồi ReadWriteLock, StampedLock và AQS (12) khi cần khả năng mà synchronized không có; delegation cho ConcurrentHashMap (13) — TicketFlow lên v2 với compute nguyên tử per-key; rồi mổ nội thất ConcurrentHashMap per-bin cùng CopyOnWrite (14) để hiểu vì sao concurrent collection nhanh; BlockingQueue và producer–consumer (15); rồi các synchronizer điều phối tiến độ (16).

Phần 3 — Kiến trúc (bài 17–22): khi công cụ đã đủ, câu hỏi đổi từ "khóa thế nào" sang "tổ chức thực thi thế nào". Executor và thread pool (17) tách task khỏi thread; CompletableFuture (18) ghép task thành pipeline async — TicketFlow v3; fork/join (19) cho divide-and-conquer thuần CPU; virtual thread (20) phá cái trần kích thước pool — TicketFlow v4; bài 21 soi khi nào virtual thread bị pinning và cách di cư một hệ thống từ thread pool sang virtual thread; structured concurrency (22) đem kỷ luật vòng đời vào nhóm task, và ScopedValue (22b) khép series bằng cách truyền context ở quy mô virtual thread.

flowchart TB
    subgraph P1["Phan 1 - Nen tang (01-09)"]
        A["Thread va<br/>race condition"] --> A2["Atomicity va<br/>Visibility"] --> B["4 chien luoc<br/>thread safety"]
    end
    subgraph P2["Phan 2 - Cong cu (10-16)"]
        C["Atomic / Lock /<br/>CHM / Queue"]
    end
    subgraph P3["Phan 3 - Kien truc (17-22)"]
        D["Executor /<br/>CompletableFuture"] --> E["Virtual thread /<br/>Structured concurrency"]
    end
    B --> C --> D
    P1 -.-> V0["TicketFlow v0 -> v1"]
    P2 -.-> V2["TicketFlow v2"]
    P3 -.-> V4["TicketFlow v3 -> v4"]
    style A fill:#93C5FD
    style A2 fill:#93C5FD
    style B fill:#93C5FD
    style C fill:#FCD34D
    style D fill:#6EE7B7
    style E fill:#6EE7B7

Yêu cầu trước khi bắt đầu

  • Hoàn thành course Java Foundations — đặc biệt phần collections (HashMap, List) và exception, vì code ví dụ dùng chúng liên tục.
  • Hoàn thành course Java OO & Functional — record, lambda, Stream xuất hiện từ những bài đầu mà không giải thích lại.
  • JDK 25 (baseline của series — virtual thread, ScopedValue, StructuredTaskScope đều theo hành vi bản này) và một IDE chạy được JUnit để tự tái hiện race condition.

Time budget

BàiChủ đềPhút
01Process và thread~13
01bMô hình ánh xạ thread~10
02Thread API và vòng đời~13
02bCooperative cancellation~13
03Thread safety — shared mutable state~15
04Atomicity — compound action & race condition~15
05Visibility — reordering, caching, volatile~13
06Confinement~13
06bThreadLocal và ScopedValue~14
07Immutability~12
07bInitialization safety của final~9
08volatile & synchronized~13
08bBên trong synchronized~15
09wait/notify & guarded blocks~13
10Atomic & CAS~14
10bLongAdder & VarHandle~12
11ReentrantLock~13
11bCondition — nhiều wait set~11
12ReadWriteLock & StampedLock~13
12bAQS~11
13Delegation & khi nó vỡ~10
13bCompound action trên ConcurrentMap~13
14ConcurrentHashMap nội thất & CopyOnWrite~13
15Blocking queue & producer–consumer~15
16Synchronizers — ba máy đếm~13
16bExchanger và Phaser~11
17Executor & thread pool~13
17bVận hành pool production~14
18Future, FutureTask & CompletionService~14
18bCompletableFuture pipeline~14
19Fork/Join~14
20Virtual threads~13
21Virtual threads — pinning & di cư~14
22Structured concurrency~14
22bScopedValue ở quy mô virtual thread~13

Tổng cộng khoảng 7,5 giờ đọc (cộng tổng quan và tổng kết là ~7,9 giờ) — mỗi bài giờ vừa một lần ngồi 10–15 phút. Nếu bạn chạy lại các ví dụ race condition và tự sửa TicketFlow theo từng phiên bản — rất nên — hãy dự trù 10 tới 11 giờ cho cả module, chia thành nhiều phiên ngắn.

Cách học module này hiệu quả

  1. Chạy code race nhiều lần, không phải một lần. Race condition phụ thuộc lập lịch: một lần chạy ra kết quả đúng không chứng minh code đúng. Chạy ví dụ lỗi 10–20 lần (hoặc bọc trong vòng lặp), quan sát kết quả dao động — cảm giác "lúc đúng lúc sai" chính là bài học.
  2. Vẽ timeline interleaving bằng tay. Với mỗi bug trong bài, lấy giấy kẻ hai cột Thread A / Thread B rồi xếp từng bước đọc–kiểm–ghi theo thứ tự xui xẻo nhất. Kỹ năng trace interleaving này là thứ phỏng vấn senior kiểm tra, và không học được bằng cách đọc suông.
  3. Theo sát TicketFlow qua từng phiên bản. Mỗi lần capstone lên version mới, dừng lại tự hỏi: phiên bản trước hỏng ở đâu, công cụ mới vá đúng chỗ nào, và trả giá gì. So sánh giữa các version đáng giá hơn ghi nhớ API.
  4. Đừng học thuộc API, học tiêu chí chọn. java.util.concurrent có hàng chục class; thứ đọng lại phải là bảng quyết định "tình huống nào → công cụ nào" (bài tổng kết có cheat sheet đúng cho việc này).
  5. Phần 1 chậm mà chắc. Bộ ba bài 03–05 (thread safety, atomicity, visibility) là móng của mọi bài sau — nếu ranh giới giữa atomicity và visibility còn mơ hồ, các bài lock và atomic sẽ thành học vẹt. Đọc lại bài 04 và 05 cạnh nhau lần thứ hai trước khi vào phần 2 là khoản đầu tư hời.

Bài đầu tiên: Process và Thread

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Process và Thread — từ nền tảng hệ điều hành đến Java