Part 01 · Java Core · Collections · 1.1.03.04

Thread-safe method chưa đủ cho compound invariant

Chọn concurrent collection từ contention, iteration semantics và backpressure. Nhiều race xảy ra giữa hai method calls dù từng call riêng lẻ thread-safe.

9 mục nguồn2 bảng nguồn3 interview scenarios
Cách đọc: nội dung nền được giữ từ BK; phần mở rộng có nhãn Bổ sung. API contract theo Java SE 21; implementation detail đối chiếu OpenJDK tag jdk-21-ga. Không coi hằng số, layout nội bộ hoặc kết quả đo là guarantee cho mọi JDK. Các labs bổ sung tách assertion contract khỏi quan sát implementation.

1. Ba kiểu iterator semantics

KiểuBehaviorVí dụ
Fail-fastBest-effort phát hiện structural modification và có thể ném ConcurrentModificationException.ArrayList, HashMap iterators.
SnapshotDuyệt snapshot cố định, không thấy writes sau lúc tạo iterator.CopyOnWriteArrayList.
Weakly consistentKhông fail-fast; có thể thấy một phần updates, không snapshot toàn cục.ConcurrentHashMap, ConcurrentLinkedQueue.
Fail-fast không phải synchronization: không được dùng ConcurrentModificationException như cơ chế phát hiện race. Nó chỉ giúp bắt bug theo best effort.

Đối chiếu nguồn: [C4] [C16] [C17]

Bổ sung · Scope guarantee · Iterator không đóng băng object

Snapshot của CopyOnWriteArrayList cố định dãy references lúc tạo iterator; object mutable được tham chiếu vẫn có thể đổi. Iterator của nó không hỗ trợ remove/set/add. Weakly consistent không phải một ảnh chụp nhất quán của toàn collection; đừng dùng tổng tính bằng iteration đang có writes để đối soát business. Fail-fast cũng có thể xảy ra do sửa collection trong cùng thread, không chỉ race đa luồng.

[C4] [C16] [C17]

2. ConcurrentHashMap mental model

Java 8+ không dùng segmented architecture cũ như câu trả lời phổ biến. Reads phần lớn không khóa; updates phối hợp bằng CAS và synchronization ở bin khi cần, với forwarding nodes trong resize. Tree bins xử lý collision dày. Đây là implementation-level mental model; API guarantee quan trọng hơn chi tiết lock.

Đối chiếu nguồn: [C1] [C2]

Bổ sung · Happens-before · Per-key, không phải transaction

Khi get(key) trả về một value non-null của update đã công bố, update đó có happens-before với retrieval quan sát value ấy. Điều này không bảo đảm lần đọc hai keys thấy cùng một thời điểm, và không bảo vệ các lần sửa field mutable của value sau khi publish. Đọc size() để hiển thị metric được; dùng nó để cấp quota “không bao giờ quá N” thì không.

Không gọi toàn bộ ConcurrentHashMap là lock-free: đó không phải API guarantee cho mọi method. OpenJDK 21 có CAS khi phù hợp, synchronization ở bin và phối hợp chuyển dữ liệu khi resize. concurrencyLevel không còn mang nghĩa “số segments đang được khóa độc lập”.

[C1] [C2]

3. Compound race và atomic APIs

// Sai: hai threads có thể cùng thấy null rồi overwrite/lặp construction.
if (!map.containsKey(key)) {
    map.put(key, createValue());
}

// Atomic theo key cho mapping operation.
Value value = map.computeIfAbsent(key, this::createValue);

Mapping function phải ngắn, không recursively update cùng key và không thực hiện remote I/O dài. “Atomic method” không làm side effect bên ngoài atomic với map; function có exception/retry vẫn cần thiết kế riêng.

ConcurrentHashMap<String, LongAdder> counters = new ConcurrentHashMap<>();
counters.computeIfAbsent(status, ignored -> new LongAdder()).increment();

LongAdder giảm contention cho hot counters nhưng sum() dưới concurrent updates không phải atomic snapshot.

Đối chiếu nguồn: [C1] [C5] [C10]

Bổ sung · Atomic API · Chọn đúng đơn vị bất biến
Atomic mapping không đồng nghĩa atomic workflow
Mục tiêuPrimitiveGiới hạn cần nhớ
Chỉ cài mapping khi key vắngputIfAbsentAtomic việc cài; value đã được tạo trước call nên construction vẫn có thể lặp.
Tạo lười theo keyConcurrentHashMap.computeIfAbsentCallback ngắn; null/exception không thiết lập mapping. Lần gọi khác có thể thử lại.
Thay hoặc xóa đúng phiên bản đã thấyreplace(k, old, next) / remove(k, old)So value theo equality; cần version/generation khi equality chưa đủ phân biệt vòng đời.
Cộng/update một mappingcompute / mergeKhông là transaction cho nhiều key hoặc database/API bên ngoài.

Với ConcurrentHashMap Java 21, callback computeIfAbsent được gọi đúng một lần trong invocation cần tính, không gọi khi key đã có mapping. Điều này không có nghĩa “một lần trong suốt vòng đời key”: return null, exception, remove/evict rồi gọi lại đều cho phép lần tính mới. Callback không được sửa map này trong khi tính, kể cả mapping khác. Không suy rộng guarantee callback này sang mọi implementation của ConcurrentMap.

[C1] [C10]

Thiết kế: external API có thể thành công rồi callback ném lỗi, khiến map vẫn vắng nhưng side effect đã xảy ra. Bounded single-flight cần giới hạn in-flight keys, timeout, failure cleanup và idempotency; đó là protocol bổ sung, không phải chức năng miễn phí của map. Giữ remote I/O ngoài callback có thể tránh chặn bin, nhưng tự nó không chống load trùng.

LongAdder: thích hợp statistics/hot counters, không làm primitive kiểm quota. sum() không snapshot atomic; reset trong lúc ghi có thể bỏ lỡ updates. Ngoài ra, thread có thể lấy adder → thread khác remove/replace mapping → thread đầu increment adder đã bị tách. Nếu cần exact accounting, phải định nghĩa lifecycle, không xóa counters đang được sử dụng hoặc serialize lifecycle với updates.

[C5]

4. BlockingQueue và backpressure

QueueStructure/policyĐiểm cần nhớ
ArrayBlockingQueueBounded circular arrayCapacity cố định, optional fairness, predictable bound.
LinkedBlockingQueueLinked nodes, optionally boundedMặc định rất lớn; luôn chọn capacity có chủ đích.
SynchronousQueueKhông giữ elementProducer handoff trực tiếp consumer; dùng trong executor policies.
PriorityBlockingQueueUnbounded priority heapPriority không tạo backpressure; queue có thể tăng memory.
DelayQueueElements available sau delayScheduling/retry local; không durable qua process crash.

put/take block; offer/poll có immediate/timed variants. Chọn reject/block/drop/coalesce policy theo semantics. Unbounded queue thường biến overload thành latency và OOM thay vì backpressure.

Đối chiếu nguồn: [C3] [C11] [C12] [C13] [C14] [C15]

Bổ sung · Backpressure · Bound cả backlog lẫn công việc đang chạy

put chỉ chờ khi queue không nhận được phần tử, take chờ khi chưa có phần tử lấy được; với unbounded queue, put không tạo backpressure theo capacity. offer/poll immediate trả false/null nếu chưa thực hiện được; timed variants chờ trong timeout. Null không phải payload hợp lệ của BlockingQueue.

[C3]

LinkedBlockingQueue không truyền capacity có giới hạn mặc định Integer.MAX_VALUE. PriorityBlockingQueue và DelayQueue là unbounded. SynchronousQueue không có chỗ lưu phần tử: handoff cần consumer tương ứng. Fairness của ArrayBlockingQueue có thể giảm biến động/chờ đợi không công bằng, nhưng thường đổi lấy throughput; phải đo.

[C11] [C12] [C13] [C14] [C15]

Đề xuất vận hành: quy định block có timeout, reject, drop hoặc coalesce theo business; không silent-drop tác vụ cần bảo toàn. Bound số phần tử chưa đủ bound bytes hoặc công việc đã dequeue: cần thêm giới hạn payload, concurrency và in-flight. Ghi accepted/rejected/completed, queue age, wait time, depth, heap/GC; dừng consumer để kiểm overload. Tác vụ retry trong DelayQueue chỉ ở memory, cần persistence bên ngoài nếu phải sống qua restart.

Memory boundary: hành động trước khi enqueue happens-before hành động sau khi consumer lấy đúng phần tử. Không tiếp tục mutate payload dùng chung sau enqueue mà thiếu synchronization. take() thành công chưa có nghĩa business processing thành công. BlockingQueue không có giao thức close chung; định nghĩa interrupt/shutdown hoặc end marker có chủ đích.

[C3]

5. ConcurrentLinkedQueue và lock-free

Non-blocking queue dùng CAS-linked algorithm, phù hợp nhiều producers/consumers khi không cần blocking/backpressure. Lock-free nghĩa system-wide progress dưới contention, không nghĩa mọi thread luôn tiến hoặc operation không bao giờ retry. size() có thể traverse và không phù hợp hot control loop.

Đối chiếu nguồn: [C6]

Bổ sung · Race scenario · Không check-then-act bằng size

Snippet minh họa: queue là ConcurrentLinkedQueue<String>; process do ứng dụng định nghĩa.

// Khong dung size() / isEmpty() de quyet dinh poll.
String item = queue.poll();
if (item != null) {
    process(item);
}

size() là traversal, có thể không phản ánh đúng khi mutation đang diễn ra; kiểm size() < limit rồi offer cũng không tạo bounded queue. Lock-free là tiến triển ở mức hệ thống, không phải wait-free cho mỗi thread; CAS có thể retry. Queue unbounded và không nhận null; tự busy-spin chờ item còn gây tải CPU nếu không có policy chờ thích hợp.

[C6]

6. ConcurrentSkipListMap/Set

Sorted concurrent map/set dựa skip-list structure, hỗ trợ expected O(log n) lookup/update và range/navigable operations. Dùng khi cần cả concurrency lẫn sorted/range semantics; nếu chỉ lookup key, ConcurrentHashMap thường đơn giản/nhanh hơn.

Đối chiếu nguồn: [C7]

Bổ sung · Trade-off · Sorted concurrent không đồng nghĩa snapshot

Expected O(log n) là đặc tính kỳ vọng của cấu trúc, không phải latency cố định. Range query và iterator vẫn weakly consistent khi có writes; không là range transaction. Map không cho null key/value. Comparator phải giữ ordering ổn định, giống ràng buộc sorted collections.

Đặc biệt, ConcurrentSkipListMap không hứa callback computeIfAbsent được áp dụng một lần atomic như ConcurrentHashMap. Đổi implementation sau interface có thể đổi behavior callback. Ưu tiên ConcurrentHashMap khi chỉ cần key lookup là hướng chọn ban đầu, không phải kết quả benchmark đảm bảo.

[C7]

7. Backed views

Đối chiếu nguồn: [C8] [C9] [C17] [C18]

Bổ sung · Contract · View, bản sao và lifetime
Phân biệt cấu trúc được chụp và references được chia sẻ
API / kiểu viewThay đổi phản ánh thế nào?Trap
HashMap.keySet/values/entrySetBacked; removal được hỗ trợ sẽ xóa mapping.Không add key/value trực tiếp để tự đoán mapping. values().remove(v) chỉ xóa một mapping phù hợp.
List.subList(a,b)View khoảng [a,b), các thay đổi hợp lệ đi qua view phản ánh vào list.Sửa cấu trúc backing list bên ngoài view làm semantics của subList không còn xác định; không hứa luôn ném CME.
TreeMap.subMap/headMap/tailMapLive range view; thay đổi hợp lệ hai chiều.Thêm key ngoài range qua view bị từ chối; view không phải ảnh chụp.
unmodifiableList(backing)Wrapper không cho mutation qua reference đó, nhưng nhìn thấy backing đổi.Không tự thread-safe, không deep-immutable.
List.copyOf(source)Nội dung cấu trúc tách khỏi thay đổi source sau khi copy.Shallow; không nhận null; có thể reuse list đủ điều kiện, không hứa luôn allocation mới.

Không suy rộng quyền mutation của HashMap view sang tất cả maps. ConcurrentHashMap có keySet(mappedValue)/newKeySet() hỗ trợ add theo cơ chế riêng. View có thể giữ backing structure sống lâu hơn dự kiến; bản sao nhỏ phù hợp khi cần ownership độc lập. Copy từ collection đang bị ghi cũng không tự tạo một thời điểm snapshot nhất quán; cần protocol đồng bộ của nguồn.

[C1] [C8] [C9] [C17] [C18]

8. Immutable, unmodifiable và persistent

Unmodifiable nói về API mutation qua reference đó. Immutable aggregate yêu cầu observable state không đổi, gồm xử lý mutable elements. Persistent data structure tạo version mới bằng structural sharing; Java standard collections không mặc định persistent.

Đối chiếu nguồn: [C8] [C9]

Bổ sung · Ownership · Khóa đúng object hoặc publish giá trị bất biến

Snippet: traversal của synchronizedMap; không gọi I/O dài trong critical section production.

Map<String, Integer> shared =
    Collections.synchronizedMap(new HashMap<>());

// Moi access phai qua shared; khoa wrapper, khong khoa keySet().
synchronized (shared) {
    for (var entry : shared.entrySet()) {
        System.out.println(entry.getKey() + "=" + entry.getValue());
    }
}

unmodifiable là hạn chế API, không phải cơ chế memory visibility. Muốn immutable aggregate, kiểm soát cả mutable elements và các aliases còn giữ quyền sửa. Persistent structure giữ phiên bản cũ hợp lệ khi tạo phiên bản mới bằng structural sharing; các khái niệm này độc lập với thread-safety của payload. Khi business cần một snapshot nhiều key, có thể publish một aggregate bất biến qua reference có safe-publication hoặc dùng lock/transaction bao trùm invariant.

[C9] [C16]

9. Interview scenarios

ConcurrentHashMap có khóa toàn map khi put?
Không trong Java hiện đại. Reads phần lớn không khóa; updates dùng CAS và synchronization ở bin khi cần. Resize được phối hợp giữa threads. Tránh trả lời bằng “16 segments” vì đó là kiến trúc Java 7-era.
computeIfAbsent có thể gọi external API?
Không nên. Computation có thể block contention trên key/bin, retry/exception semantics khó kiểm soát, và map atomicity không bao gồm external side effect. Dùng bounded single-flight/cache loader được thiết kế rõ.
Tại sao ConcurrentHashMap không cho null?
Trong concurrent access, get()==null cần biểu diễn unambiguously rằng không có completed mapping; null value làm caller không phân biệt absent với mapped-null trong race.

Đối chiếu nguồn: [C1] [C2]

Bổ sung · Câu trả lời cần evidence

Với câu hỏi lock, tách API guarantee khỏi đường triển khai. Với callback, vẽ cửa sổ external effect đã thành công nhưng mapping chưa tồn tại. Với null, nói về trạng thái quan sát tại retrieval, không hứa key sẽ vắng ở call kế tiếp. Ba câu hỏi trên được giữ nguyên; các race lab bên dưới kiểm tra hệ quả thay vì học thuộc thuật ngữ.

[C1] [C2]

10. Race labs: điều khiển interleaving, không chờ may rủi

Bổ sung thực hành · Ba chương trình độc lập, Java 21. Dùng barrier/latch thay vì sleep để tạo thứ tự mong muốn. Timeout chỉ ngăn lab treo; không là latency SLO.

Lab D1 · Hai method thread-safe vẫn tạo construction trùng

Failure-first: buộc hai threads cùng đọc absent trước khi put. Sau đó giữ cùng mục tiêu nhưng đổi sang computeIfAbsent; callback chỉ tăng counter local, không remote I/O.

CompoundRaceLab.java

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class CompoundRaceLab {
    static int run(boolean atomic) throws Exception {
        var map = new ConcurrentHashMap<String, Integer>();
        var constructions = new AtomicInteger();
        var barrier = new CyclicBarrier(2);

        Callable<Void> task = () -> {
            if (atomic) {
                barrier.await(5, TimeUnit.SECONDS);
                map.computeIfAbsent("key",
                    ignored -> constructions.incrementAndGet());
            } else {
                boolean absent = !map.containsKey("key");
                barrier.await(5, TimeUnit.SECONDS); // Both observed absent.
                if (absent) map.put("key", constructions.incrementAndGet());
            }
            return null;
        };

        try (var pool = Executors.newFixedThreadPool(2)) {
            Future<Void> a = pool.submit(task);
            Future<Void> b = pool.submit(task);
            a.get(10, TimeUnit.SECONDS);
            b.get(10, TimeUnit.SECONDS);
        }
        if (map.size() != 1) throw new AssertionError("mapping count");
        return constructions.get();
    }

    public static void main(String[] args) throws Exception {
        int raced = run(false);
        int fixed = run(true);
        if (raced != 2 || fixed != 1) {
            throw new AssertionError("Unexpected construction counts");
        }
        System.out.printf("check-then-put=%d, computeIfAbsent=%d%n",
            raced, fixed);
        System.out.println("PASS: per-method safety vs compound invariant");
    }
}
javac --release 21 CompoundRaceLab.java
java CompoundRaceLab

Quan sát / acceptance: map cuối đều có một mapping, nhưng construction counts là 2 ở check-then-put và 1 ở computeIfAbsent. Counter construction mới là evidence lỗi; map.size()==1 không đủ. Nộp: stdout và timeline của hai threads. Không suy rộng kết quả này thành exactly-once external side effect.

[C1] [C10]

Lab D2 · Backed view, shallow snapshot và queue đầy

Failure-first: sửa backing sau khi tạo wrapper/copy; sửa object nằm trong bản copy; thêm phần tử COW sau khi tạo iterator; offer vào bounded queue đã đầy mà không có consumer.

ViewsQueueLab.java

import java.util.*;
import java.util.concurrent.*;

public class ViewsQueueLab {
    static void check(boolean ok, String message) {
        if (!ok) throw new AssertionError(message);
    }

    public static void main(String[] args) {
        var base = new ArrayList<>(List.of(1, 2));
        var view = Collections.unmodifiableList(base);
        var copy = List.copyOf(base);
        base.add(3);
        check(view.size() == 3 && copy.size() == 2, "view vs copy");
        base.subList(0, 2).set(0, 99);
        check(base.get(0) == 99, "subList is backed");

        var text = new StringBuilder("a");
        var shallow = List.copyOf(List.of(text));
        text.append("b");
        check(shallow.get(0).toString().equals("ab"), "shallow copy");

        var map = new HashMap<String, Integer>();
        map.put("A", 1);
        map.keySet().remove("A");
        check(map.isEmpty(), "keySet removal must affect map");

        var cow = new CopyOnWriteArrayList<>(List.of("old"));
        var iterator = cow.iterator();
        cow.add("new");
        check(iterator.next().equals("old") && !iterator.hasNext(),
            "snapshot iterator must not see later addition");

        var queue = new ArrayBlockingQueue<String>(1);
        check(queue.offer("first"), "first offer");
        check(!queue.offer("second"), "full queue must reject immediate offer");
        check("first".equals(queue.poll()) && queue.poll() == null,
            "poll and empty result");
        System.out.println("PASS: backed, shallow, snapshot, bounded queue");
    }
}
javac --release 21 ViewsQueueLab.java
java ViewsQueueLab

Acceptance: wrapper nhìn thấy phần tử mới nhưng copy không thấy thay đổi cấu trúc; StringBuilder trong copy vẫn đổi; remove qua keySet xóa mapping; iterator COW không thấy phần tử mới; lần offer thứ hai trả false. Nộp: stdout và policy xử lý false; không thay bounded queue bằng queue unbounded rồi gọi đó là sửa overload.

[C3] [C4] [C8] [C9] [C17]

Lab D3 · Counter bị tách khỏi mapping

Failure-first: worker giữ LongAdder cũ; luồng điều khiển remove rồi thay mapping; worker mới increment. Từng thao tác đều hợp lệ nhưng metric đọc từ map không chứa increment vừa xong.

CounterLifecycleLab.java

import java.util.concurrent.*;
import java.util.concurrent.atomic.LongAdder;

public class CounterLifecycleLab {
    public static void main(String[] args) throws Exception {
        var counters = new ConcurrentHashMap<String, LongAdder>();
        var old = new LongAdder();
        counters.put("ok", old);
        var loaded = new CountDownLatch(1);
        var replaced = new CountDownLatch(1);

        try (var pool = Executors.newSingleThreadExecutor()) {
            Future<?> worker = pool.submit(() -> {
                LongAdder reference =
                    counters.computeIfAbsent("ok", ignored -> new LongAdder());
                loaded.countDown();
                try {
                    if (!replaced.await(5, TimeUnit.SECONDS)) {
                        throw new AssertionError("replacement timeout");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException(e);
                }
                reference.increment(); // Increments the detached object.
            });

            if (!loaded.await(5, TimeUnit.SECONDS)) {
                throw new AssertionError("load timeout");
            }
            try {
                counters.remove("ok", old);
                counters.put("ok", new LongAdder());
            } finally {
                replaced.countDown();
            }
            worker.get(10, TimeUnit.SECONDS);
        }
        long visible = counters.get("ok").sum();
        if (old.sum() != 1 || visible != 0) {
            throw new AssertionError("Unexpected lifecycle observation");
        }
        System.out.printf("detached=%d, mapped=%d%n", old.sum(), visible);
        System.out.println("PASS: lifecycle race, not a LongAdder increment bug");
    }
}
javac --release 21 CounterLifecycleLab.java
java CounterLifecycleLab

Acceptance: detached=1, mapped=0. Đây là kết quả interleaving do lab cố ý tạo, không là mất increment bên trong LongAdder. Nộp: timeline và bản sửa có lifecycle rõ: không remove active counter, hoặc mọi update/removal cùng protocol khóa/compute; không còn writer giữ reference ngoài protocol. Chạy lại bản sửa để chứng minh cả lifecycle lẫn tổng sau khi quiescent.

[C1] [C5]

11. Review checklist: từ data structure tới business invariant

Acceptance gate cho concurrent collections và views
Câu cần trả lờiEvidence đạt yêu cầuRed flag
Atomic theo đơn vị nào?Timeline check-then-act và test atomic API; mô tả invariant nhiều key riêng.“ConcurrentHashMap nên mọi workflow đều an toàn.”
Value có mutable không?Test concurrent writers vào value; có immutable replacement hoặc value-level protocol.computeIfAbsent(... new ArrayList()).add(...) từ nhiều threads.
Lifecycle của mapping/counter?Drill remove/recreate khi còn writer; tổng sau quiescence được đối soát.Đổi LongAdder nhưng quên references đã phát ra.
Overload được chặn ở đâu?Dừng consumer; accepted/rejected/in-flight, age, heap và timeout đều có evidence.Unbounded queue hoặc size() < limit rồi offer.
View/copy/iterator nhìn thấy gì?Tests backing mutation, element mutation và iterator creation time.Dùng unmodifiable làm đồng nghĩa deep-immutable hoặc thread-safe.
Snapshot hoặc transaction nhiều key?Một cơ chế lock/version/immutable aggregate thống nhất; readers cũng theo protocol.Lấy hai get hoặc copy map đang bị ghi rồi gọi đó là snapshot atomic.
Definition of Done cho bài này: giữ được lập luận cho cả ba câu phỏng vấn nguồn; chạy và giải thích ba lab bổ sung; phân biệt data-structure safety, visibility, lifecycle và business correctness. Một stress test không thấy lỗi không chứng minh absence of race; cần chứng minh protocol và dùng test để kiểm các interleavings đã xác định.

12. Tài liệu chính thức và phạm vi tham chiếu

Kiểm chứng ngày 12/09/2026. API được ghim Java SE 21; source được ghim tag jdk-21-ga, không phải nhánh phát triển mới nhất. Các liên kết chỉ để đọc tham khảo; trang không tải tài nguyên ngoài.

  1. [C1] ConcurrentHashMap API — API: happens-before, compute, aggregate operations. Giữ từ nguồn BK.
  2. [C2] OpenJDK 21 source — Implementation: bins, CAS, synchronization, cooperative resize. Giữ từ nguồn BK.
  3. [C3] BlockingQueue API — API: blocking/timed operations, memory consistency. Giữ từ nguồn BK.
  4. [C4] CopyOnWriteArrayList API — API: snapshot iterator, shallow element references. Tham khảo bổ sung.
  5. [C5] LongAdder API — Scope: sum/reset không là atomic snapshot. Tham khảo bổ sung.
  6. [C6] ConcurrentLinkedQueue API — API: non-blocking queue, size, null, weak iteration. Tham khảo bổ sung.
  7. [C7] ConcurrentSkipListMap API — API: sorted/range, expected complexity, callback caveat. Tham khảo bổ sung.
  8. [C8] List API — API: subList, copyOf, null và shallow copy. Tham khảo bổ sung.
  9. [C9] Collections API — API: unmodifiableList, synchronizedMap và traversal. Tham khảo bổ sung.
  10. [C10] ConcurrentMap API — API: conditional replace/remove/putIfAbsent. Tham khảo bổ sung.
  11. [C11] ArrayBlockingQueue API — API: bounded capacity và fairness. Tham khảo bổ sung.
  12. [C12] LinkedBlockingQueue API — API: default capacity và linked nodes. Tham khảo bổ sung.
  13. [C13] SynchronousQueue API — API: rendezvous, không có storage. Tham khảo bổ sung.
  14. [C14] PriorityBlockingQueue API — API: unbounded priority queue, tie handling. Tham khảo bổ sung.
  15. [C15] DelayQueue API — API: unbounded delayed elements. Tham khảo bổ sung.
  16. [C16] java.util.concurrent package — API: concurrent collections và memory consistency. Tham khảo bổ sung.
  17. [C17] HashMap API — Đối chiếu fail-fast và map views. Tham khảo bổ sung.
  18. [C18] TreeMap API — Đối chiếu range-view bounds và backed semantics. Tham khảo bổ sung.