Danh mục:
Giới thiệu
Bài viết này giới thiệu về Lazy Initialization — một kỹ thuật phổ biến trong Java để trì hoãn việc tạo đối tượng cho đến khi nó thực sự cần sử dụng.
Vấn đề ban đầu
Trong ví dụ tiêu chuẩn sau:
class Demo { private Collaborator collaborator = new Collaborator(); public Collaborator getCollaborator() { return collaborator; } public static void main(String[] args) { Demo demo = new Demo(); Collaborator collaborator = demo.getCollaborator(); } }
Trong đoạn này, mỗi khi Demo được tạo, đối tượng
Collaborator cũng được tạo ngay lập tức — dù có lúc bạn chưa cần dùng đến. Ý tưởng của Lazy Initialization
Để tiết kiệm tài nguyên và tránh tạo đối tượng không cần thiết, ta chỉ nên khởi tạo
Collaborator khi thực sự được yêu cầu — tức là khi getCollaborator() được gọi lần đầu. Cách làm như sau:
class Demo { private Collaborator collaborator; public Collaborator getCollaborator() { if (collaborator == null) { collaborator = new Collaborator(); } return collaborator; } }
👉 Đoạn này có nghĩa là:
Collaborator chưa được tạo khi Demo khởi tạo.Chỉ khi
getCollaborator() gọi, ta mới kiểm tra null và tạo mới nếu cần.➡️ Đây chính là Lazy Initialization.
Nhược điểm trong môi trường đa luồng
Trong môi trường chạy đa luồng (multi-thread), đoạn code trên có thể gây lỗi như:
Hai luồng cùng gọi
getCollaborator() đồng thời khi collaborator == null,Khi đó cả hai luồng có thể tạo hai đối tượng khác nhau, phá vỡ mong muốn chỉ có một.
Giải pháp với synchronized
Để đảm bảo an toàn trong đa luồng, ta có thể sử dụng
synchronized:public synchronized Collaborator getCollaborator() { if (collaborator == null) { collaborator = new Collaborator(); } return collaborator; }
➡️ Điều này đảm bảo rằng chỉ một luồng tại một thời điểm được phép tạo Collaborator.
Tối ưu với Double-Check Locking
Đoạn code synchronized trên có thể làm giảm hiệu năng vì mỗi lần gọi đều lock. Ta có thể tối ưu bằng cách dùng Double-Check Locking:
public Collaborator getCollaborator() { if (collaborator == null) { synchronized(this) { if (collaborator == null) { collaborator = new Collaborator(); } } } return collaborator; }
Ngoại lệ: để an toàn hơn trong Java, trường collaborator cần được khai báo volatile.
➡️ Đây là cách phổ biến để đảm bảo chỉ tạo một đối tượng duy nhất ngay cả trong nhiều luồng cùng lúc.
Tóm tắt ý chính
- Lazy Initialization là kỹ thuật trì hoãn tạo đối tượng cho đến khi cần dùng.
- Giúp tiết kiệm tài nguyên, bộ nhớ và chi phí khởi tạo nếu đối tượng nặng.
✅ Cần chú ý đồng bộ hoá trong môi trường đa luồng để tránh tạo nhiều đối tượng không mong muốn.
Nguồn bài viết dịch từ ryukato.github.io