1-3-3-Cơ chế đồng bộ luồng

Cơ chế đồng bộ luồng trong Java là nền tảng cốt lõi cho phát triển ứng dụng đa luồng. Dưới đây là phân tích toàn diện về các khía cạnh kỹ thuật quan trọng:

  1. Khái niệm nền tảng về đồng bộ

Khi nhiều luồng truy cập tài nguyên chia sẻ mà không kiểm soát, sẽ xảy ra trạng thái race conditionvi phạm tính nguyên tử. Ví dụ: hai luồng cùng đọc số dư tài khoản 100, luồng A nạp thêm 50 (tổng 150), luồng B rút 30 (tổng 70) thay vì kết quả đúng là 120.

  1. Cơ chế khóa ẩn (synchronized)

// Khóa đối tượng cụ thể
public void method() {
    synchronized (obj) {
        balance += amount;
    }
}

// Khóa this (đối tượng hiện tại)
synchronized (this) {
    // Truy cập biến instance
}

// Khóa class (đối tượng tĩnh)
synchronized (MyClass.class) {
    // Truy cập biến static
}

Nguyên lý hoạt động:

  • Sử dụng Mark Word trong header object để quản lý trạng thái khóa
  • Quá trình tiến hóa: không khóa → khóa thiên vị → khóa nhẹ → khóa nặng
  • Đặc tính: độc quyền, có thể tái nhập, không thể gián đoạn
  1. Cơ chế khóa hiển thị (Lock)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private final Lock lock = new ReentrantLock();
    private int count;
    
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}

Tính năng nâng cao:

  • Khóa có thể gián đoạn (lockInterruptibly())
  • Thử khóa trong thời gian giới hạn (tryLock(timeout))
  • Khóa đọc-viết (ReadWriteLock)
  1. Biến nguyên tử

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
    private AtomicInteger count = new AtomicInteger();
    
    public void increment() {
        count.incrementAndGet();
    }
    
    public boolean compareAndSet(int expected, int value) {
        return count.compareAndSet(expected, value);
    }
}

Nguyên lý CAS:

  • So sánh giá trị hiện tại với giá trị kỳ vọng
  • Cập nhật nếu khớp, không thực hiện gì nếu không khớp
  1. Biến cục bộ luồng (ThreadLocal)

public class SessionManager {
    private static ThreadLocal<String> userContext = new ThreadLocal<>();
    
    public void setUser(String user) {
        userContext.set(user);
    }
    
    public String getUser() {
        return userContext.get();
    }
    
    public void clear() {
        userContext.remove();
    }
}
  1. So sánh các cơ chế đồng bộ

Đặc điểm synchronized ReentrantLock Atomic ThreadLocal
Loại khóa Ẩn Hiển Không khóa (CAS) Không khóa (Isolation)
Tính linh hoạt Thấp Cao (có thể thử, gián đoạn, timeout) Cao Cao
Hiệu năng Tốt sau tối ưu Tốt Tốt nhất Tốt nhất
Tính tái nhập Không áp dụng Không áp dụng
  1. Chủ đề nâng cao và thực tiễn

Vấn đề khóa chết:

// Luồng 1 giữ lockA đợi lockB
synchronized (lockA) {
    synchronized (lockB) { /* ... */ }
}

// Luồng 2 giữ lockB đợi lockA
synchronized (lockB) {
    synchronized (lockA) { /* ... */ }
}

Chiến lược phòng tránh:

  1. Sắp xếp thứ tự khóa cố định
  2. Sử dụng tryLock() có timeout
  3. Giảm độ mịn khóa (lock granular)
  4. Ưu tiên tập hợp đồng bộ sẵn có

Bẫy thường gặp:

Bẫy Triệu chứng Giải pháp
Khóa chết Luồng bị treo vĩnh viễn Thứ tự khóa thống nhất, sử dụng cơ chế thử khóa
Khóa sống Luồng liên tục retry nhưng thất bại Thêm backoff ngẫu nhiên, điều chỉnh chiến lược retry
Thiếu tài nguyên Một số luồng không nhận được tài nguyên Khóa công bằng, điều chỉnh độ ưu tiên
  1. Lời khuyên thiết kế hệ thống

  • Hiệu năng cao: Ưu tiên biến nguyên tử và khóa đọc-viết
  • Độ phức tạp: Dùng synchronized cho đơn giản, Lock cho phức tạp
  • Dễ bảo trì: Ghi nhớ giải phóng khóa trong khối finally
  • Mở rộng: Chọn giải pháp có khả năng mở rộng tốt

Trong hệ thống phân tán, cần kết hợp với khóa phân tán (Redis, ZooKeeper) và giao dịch phân tán (TCC, Seata). Điều này đòi hỏi kiến trúc sư phải cân bằng giữa tính nhất quán và hiệu năng hệ thống.

Thẻ: đồng bộ luồng Java lập trình đa luồng công cụ JUC tối ưu hiệu năng

Đăng vào ngày 11 tháng 7 lúc 10:10