Singleton Pattern trong Java
Singleton Pattern
là một trong những Design Patterns nổi bật trong nhóm Creational Pattern
, giúp quản lý việc tạo đối tượng trong một ứng dụng.
Được sử dụng để đảm bảo rằng một lớp chỉ có duy nhất một instance (thể hiện)
và cung cấp một điểm truy cập toàn cục đến instance
đó, Singleton
được áp dụng phổ biến trong các ứng dụng khi cần chia sẻ tài nguyên hoặc quản lý trạng thái chung.
1. Ưu điểm
- Tiết kiệm tài nguyên:
Singleton
giúp hạn chế việc tạo nhiều đối tượng không cần thiết, từ đó tiết kiệm tài nguyên hệ thống. - Quản lý nhất quán: Với một
instance
duy nhất, trạng thái chung được quản lý dễ dàng và nhất quán. - Dễ triển khai và sử dụng: Cách triển khai đơn giản và rõ ràng giúp lập trình viên dễ dàng tiếp cận và tích hợp.
2. Nhược điểm
- Khó khăn trong việc mở rộng: Việc mở rộng lớp
Singleton
có thể gặp khó khăn do ràng buộc instance duy nhất. - Khả năng gây ra vấn đề trong môi trường đa luồng:
Singleton
không được triển khai đúng cách có thể dẫn đến race condition và các lỗi liên quan đếnconcurrency
. - Phụ thuộc vào trạng thái toàn cục: Nếu không được quản lý cẩn thận,
Singleton
có thể dẫn đến việc chia sẻ trạng thái không mong muốn, gây khó khăn trong việc debug và bảo trì.
3. Implement
Singleton Pattern
thường được áp dụng trong các tình huống như:
- Quản lý kết nối cơ sở dữ liệu: Đảm bảo rằng chỉ có một kết nối duy nhất đến cơ sở dữ liệu tại một thời điểm.
- Cấu hình ứng dụng: Lưu trữ các thông tin cấu hình chung mà nhiều thành phần khác nhau của ứng dụng cần truy cập.
- Logging: Duy trì một logger duy nhất để ghi lại các thông tin log trong ứng dụng.
- Cơ chế cache: Quản lý cache tập trung để tối ưu hóa việc truy xuất dữ liệu.
Có nhiều cách để triển khai Singleton Pattern
trong Java, từ cách đơn giản nhất đến những cách tối ưu hóa cao cấp hơn để hỗ trợ môi trường đa luồng.
Nhưng dù cho việc implement bằng cách nào đi nữa cũng dựa vào nguyên tắc dưới đây cơ bản dưới đây:
- private constructor để hạn chế truy cập từ class bên ngoài.
- Đặt private static final variable đảm bảo biến chỉ được khởi tạo trong class.
- Có một method public static để return instance được khởi tạo ở trên.
Dưới đây là một số cách triển khai phổ biến.
3.1 Singleton cơ bản
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BasicSingleton {
private static BasicSingleton instance;
private BasicSingleton() {
// Ngăn không cho khởi tạo từ bên ngoài
}
public static BasicSingleton getInstance() {
if (instance == null) {
instance = new BasicSingleton();
}
return instance;
}
}
Cách triển khai này đơn giản, dễ hiểu nhưng không an toàn trong môi trường đa luồng.
Nếu hai luồng gọi getInstance()
cùng lúc, có thể dẫn đến việc tạo ra hai instance
.
3.2 Singleton với từ khóa synchronized
1
2
3
4
5
6
7
8
9
10
11
12
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
Tuy cách này đảm bảo an toàn trong môi trường đa luồng. Tuy nhiên sử dụng từ khóa synchronized
khiến hiệu suất giảm trong trường hợp getInstance()
được gọi thường xuyên.
3.3 Double-checked locking
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DCLSingleton {
private static volatile DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
Nhận xét đây là cách triển khai tối ưu, tránh được việc đồng bộ hóa liên tục, nâng cao hiệu suất. Nhược điểm duy nhất là pức tạp hơn so với các cách khác.
3.4 Singleton sử dụng Enum
1
2
3
4
5
6
7
public enum EnumSingleton {
INSTANCE;
public void showMessage() {
System.out.println("Hello from Singleton!");
}
}
Cách tiếp cận này là an toàn nhất, đảm bảo chống lại việc phá vỡ Singleton trong Java nhờ đặc tính của Enum. Nhưng không linh hoạt nếu cần khởi tạo Singleton với tham số.
4. Ví dụ bonus thêm
Giả sử bạn cần một Logger duy nhất trong toàn bộ ứng dụng:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Logger {
private static Logger instance;
private Logger() {}
public static Logger getInstance() {
if (instance == null) {
synchronized (Logger.class) {
if (instance == null) {
instance = new Logger();
}
}
}
return instance;
}
public void log(String message) {
System.out.println("[LOG] " + message);
}
}
// Cách sử dụng
public class MainApp {
public static void main(String[] args) {
Logger logger = Logger.getInstance();
logger.log("Application started");
}
}
Lời kết
Singleton Pattern
là một trong những pattern đơn giản nhưng hiệu quả, thường xuyên xuất hiện trong các dự án thực tế.
Khi triển khai Singleton
, cần cân nhắc đến môi trường đa luồng và hiệu suất của ứng dụng để chọn phương pháp phù hợp nhất.
Nhờ vào việc quản lý tài nguyên tốt và cung cấp tính nhất quán, Singleton
vẫn luôn là một lựa chọn đáng cân nhắc trong các tình huống sử dụng cụ thể.
Bài viết mang tính chất “ghi chú, lưu trữ, chia sẻ và phi lợi nhuận”.
Nếu bạn thấy hữu ích, đừng quên chia sẻ với bạn bè và đồng nghiệp của mình nhé!
Happy coding! 😎 👍🏻 🚀 🔥