Module 03 · Executable

Java Concurrency Foundation

Nền tảng bắt buộc trước async/reactive: threads, synchronization, pools, deadlock và Virtual Threads.


1. Mục tiêu học

  • Phân biệt concurrency và parallelism.
  • Nhìn được chi phí của platform thread và context switching.
  • Hiểu race condition, visibility, atomicity.
  • Biết dùng Executor/ThreadPoolExecutor theo tư duy resource management.
  • Hiểu Virtual Threads giải quyết bài toán gì và không giải quyết gì.

2. Mental model: server truyền thống

Request A ──► Thread A ──► DB call ──[WAIT]──► response
Request B ──► Thread B ──► API call ─[WAIT]──► response
Request C ──► Thread C ──► CPU work ─────────► response

Điểm quan trọng: khi một platform thread block vì I/O, OS thread bên dưới vẫn là resource bị giữ cho computation đó. Nhiều I/O wait có thể dẫn tới nhiều threads, scheduling overhead và memory footprint.

Không suy ra: “thread xấu”. Thread-per-request vẫn rất phù hợp trong nhiều hệ thống, đặc biệt với Virtual Threads.

3. Concurrency vs Parallelism

Khái niệmÝ nghĩaVí dụ
ConcurrencyNhiều công việc đang tiến triển trong cùng khoảng thời gian.Một CPU core xen kẽ xử lý nhiều task.
ParallelismNhiều công việc thực thi thực sự cùng lúc.4 CPU core chạy 4 computation đồng thời.

Một chương trình có thể concurrent nhưng không parallel. Reactive/event-loop thường tối ưu concurrency cho I/O; CPU-heavy work vẫn cần tài nguyên tính toán thực.

4. Thread lifecycle và interrupt

Thread worker = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        doSmallUnitOfWork();
    }
});
worker.start();
worker.interrupt();
worker.join();

interrupt() là cơ chế cooperative cancellation, không phải “kill thread”. Code phải tôn trọng interrupt hoặc xử lý InterruptedException đúng cách.

5. Race condition, atomicity, visibility

class Counter {
    private int value = 0;
    void increment() { value++; }
    int get() { return value; }
}

value++ không phải một thao tác atomic duy nhất. Nhiều thread có thể đọc cùng giá trị rồi ghi đè kết quả của nhau.

Sửa bằng synchronized

class Counter {
    private int value;
    synchronized void increment() { value++; }
    synchronized int get() { return value; }
}

volatile dùng cho visibility, không biến compound operation thành atomic

volatile boolean running = true;
volatile int value; value++; vẫn có race condition.

6. Executor và Thread Pool

Executor tách “submit task” khỏi cơ chế thực thi. ThreadPoolExecutor giúp giới hạn và quản lý resource.

ExecutorService pool = new ThreadPoolExecutor(
    4, 8,
    30, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(100),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

Luồng quyết định đơn giản

submit task
   │
   ├─ core threads còn chỗ? ─► create/use worker
   │
   ├─ queue còn chỗ? ────────► enqueue
   │
   ├─ chưa tới max? ─────────► create extra worker
   │
   └─ hết capacity ──────────► rejection policy

Pool không chỉ là performance trick; nó là cơ chế resource bounding.

7. Deadlock

synchronized (lockA) {
    synchronized (lockB) { ... }
}
// thread khác lấy lockB rồi lockA => nguy cơ deadlock

Phòng tránh bằng lock ordering nhất quán, giảm critical section, timeout lock khi phù hợp và dùng thread dump để chẩn đoán.

8. Virtual Threads

Virtual Thread là lightweight Java thread được JVM scheduling lên platform threads. Mục tiêu chính là giúp mô hình thread-per-task/thread-per-request scale tốt hơn cho workload nhiều blocking I/O.

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    Future<String> a = executor.submit(() -> callServiceA());
    Future<String> b = executor.submit(() -> callServiceB());
    System.out.println(a.get() + b.get());
}
Mental model: Virtual Threads làm blocking style rẻ hơn; chúng không biến CPU-bound code thành nhanh hơn và không tạo thêm CPU core.

So sánh sơ bộ

ModelƯu điểmTrade-off
Platform thread poolQuen thuộc, kiểm soát resource rõ.Nhiều blocking task có thể cần nhiều OS threads.
Virtual ThreadsImperative code, concurrency lớn cho blocking I/O.Vẫn cần hiểu resource downstream; CPU/pinning/lock contention không biến mất.
Event LoopÍt threads, hiệu quả cho non-blocking I/O.Blocking event loop gây tail latency nghiêm trọng.

9. Lab — Saturation của thread pool

Tạo pool 2 threads, queue 2, rồi submit 10 task ngủ 2 giây. In timestamp, thread name và task ID.

ExecutorService pool = new ThreadPoolExecutor(
    2, 2, 0, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(2),
    new ThreadPoolExecutor.AbortPolicy()
);

for (int i = 0; i < 10; i++) {
    int id = i;
    try {
        pool.submit(() -> {
            System.out.printf("%s task=%d%n",
                Thread.currentThread().getName(), id);
            Thread.sleep(2000);
            return null;
        });
    } catch (RejectedExecutionException e) {
        System.out.println("REJECTED task=" + id);
    }
}

Quan sát: task nào chạy, task nào queue, task nào bị reject. Sau đó đổi sang CallerRunsPolicy và giải thích backpressure “thô” xuất hiện như thế nào ở phía producer.

10. Bài tập

C1. Một server có 200 platform threads và mỗi request dành 95% thời gian đợi DB. Tăng pool lên 2.000 có chắc tăng throughput 10 lần không? Giải thích ít nhất 4 bottleneck khác có thể xuất hiện.
Gợi ý: DB connection pool, DB capacity, memory/stack, scheduling/context switching, downstream rate limit.
C2. Vì sao volatile int counter không đủ cho counter++?
Visibility không đảm bảo read-modify-write atomic.
C3. Viết lại một đoạn fixed thread pool gọi 1.000 HTTP blocking requests bằng virtual-thread-per-task executor; đo thread count và wall-clock time.
C4. Tạo deadlock có chủ đích, chụp thread dump, xác định hai lock gây cycle.

11. Interview checkpoint

  1. Concurrency khác parallelism thế nào?
  2. Context switching có chi phí gì?
  3. synchronized giải quyết visibility và atomicity ra sao?
  4. volatile phù hợp cho pattern nào?
  5. Tại sao bounded queue quan trọng?
  6. Virtual Threads khác thread pool truyền thống ở đâu?
  7. Virtual Threads có làm CPU-bound code nhanh hơn không?
  8. Khi nào event loop có lợi hơn blocking thread model?

12. Checklist

  • ☑ Hiểu thread là resource, không chỉ là syntax.
  • ☑ Biết pool = scheduling + bounding.
  • ☑ Không nhầm volatile với atomicity.
  • ☑ Hiểu virtual thread tối ưu concurrency cho blocking I/O.
  • ☑ Sẵn sàng học Future/CompletableFuture.

Tài liệu và code thực hành

Đọc chapter chi tiết [legacy source: modules/03-java-concurrency.md] · Mở foundation labs [legacy source: labs/foundation/README.md] · Xem Java source [legacy source: examples/README.md]