Singleton là gì?
Singleton là một trong những mẫu thiết kế đơn giản nhất có thể áp dụng cho một lớp. Đôi khi mọi người nói “lớp này là một singleton”, nghĩa là lớp này triển khai mẫu thiết kế singleton. Đôi khi cần phải viết một lớp mà chỉ có thể tạo một đối tượng. Ví dụ: một lớp chịu trách nhiệm ghi nhật ký hoặc kết nối với cơ sở dữ liệu. Mẫu thiết kế Singleton mô tả cách chúng ta có thể hoàn thành nhiệm vụ đó. Singleton là một mẫu thiết kế thực hiện hai việc:-
Cung cấp sự đảm bảo rằng một lớp sẽ chỉ có một thể hiện của lớp đó.
-
Cung cấp một điểm truy cập toàn cầu cho một thể hiện của lớp này.
-
Nhà xây dựng tư nhân. Hạn chế khả năng tạo các đối tượng lớp bên ngoài chính lớp đó.
-
Một phương thức tĩnh công khai trả về một thể hiện của lớp. Phương pháp này được gọi là
getInstance
. Đây là điểm truy cập toàn cầu vào thể hiện của lớp.
Tùy chọn triển khai
Mẫu thiết kế singleton được sử dụng theo nhiều cách khác nhau. Mỗi lựa chọn đều tốt và xấu theo cách riêng của nó. Ở đây, như mọi khi: không có lý tưởng nào cả, nhưng bạn cần phải phấn đấu vì nó. Nhưng trước hết, hãy xác định điều gì là tốt và điều gì là xấu cũng như số liệu nào ảnh hưởng đến việc đánh giá việc triển khai một mẫu thiết kế. Hãy bắt đầu với sự tích cực. Dưới đây là các tiêu chí mang lại sự thành công và hấp dẫn cho việc thực hiện:-
Khởi tạo lười biếng: khi một lớp được tải trong khi ứng dụng đang chạy chính xác khi cần thiết.
-
Tính đơn giản và minh bạch của mã: tất nhiên, số liệu này mang tính chủ quan nhưng quan trọng.
-
An toàn luồng: hoạt động chính xác trong môi trường đa luồng.
-
Hiệu suất cao trong môi trường đa luồng: các luồng chặn lẫn nhau ở mức tối thiểu hoặc hoàn toàn không chặn nhau khi chia sẻ tài nguyên.
-
Khởi tạo không lười biếng: khi một lớp được tải khi ứng dụng khởi động, bất kể nó có cần thiết hay không (một nghịch lý, trong thế giới CNTT thà lười biếng)
-
Độ phức tạp và khả năng đọc mã kém. Số liệu cũng mang tính chủ quan. Chúng tôi sẽ cho rằng nếu máu chảy ra từ mắt thì việc thực hiện chỉ ở mức bình thường.
-
Thiếu sự an toàn của chủ đề. Nói cách khác, “mối nguy hiểm về sợi chỉ”. Hoạt động không chính xác trong môi trường đa luồng.
-
Hiệu suất kém trong môi trường đa luồng: các luồng luôn chặn lẫn nhau hoặc thường xuyên khi chia sẻ tài nguyên.
Mã số
Bây giờ chúng tôi đã sẵn sàng xem xét các tùy chọn triển khai khác nhau, liệt kê những ưu và nhược điểm:Giải pháp đơn giản
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
Việc thực hiện đơn giản nhất. Ưu điểm:
-
Tính đơn giản và minh bạch của mã
-
An toàn chủ đề
-
Hiệu suất cao trong môi trường đa luồng
- Không lười khởi tạo.
Khởi tạo lười biếng
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Ưu điểm:
-
Khởi tạo lười biếng.
-
Không an toàn cho chủ đề
Trình truy cập được đồng bộ hóa
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Ưu điểm:
-
Khởi tạo lười biếng.
-
An toàn chủ đề
-
Hiệu suất kém trong môi trường đa luồng
getInstance
đã được đồng bộ hóa và bạn chỉ có thể nhập từng phương thức một. Trên thực tế, chúng ta không cần phải đồng bộ hóa toàn bộ phương thức mà chỉ cần đồng bộ hóa phần đó trong đó chúng ta khởi tạo một đối tượng lớp mới. Nhưng chúng ta không thể đơn giản gói synchronized
phần chịu trách nhiệm tạo đối tượng mới vào một khối: điều này sẽ không mang lại sự an toàn cho luồng. Nó phức tạp hơn một chút. Phương pháp đồng bộ hóa chính xác được đưa ra dưới đây:
Khóa kiểm tra kép
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
Ưu điểm:
-
Khởi tạo lười biếng.
-
An toàn chủ đề
-
Hiệu suất cao trong môi trường đa luồng
-
Không được hỗ trợ trên các phiên bản Java thấp hơn 1.5 (từ khóa dễ bay hơi đã được sửa trong phiên bản 1.5)
INSTANCE
phải là final
, hoặc volatile
. Việc triển khai cuối cùng mà chúng ta sẽ thảo luận hôm nay là Class Holder Singleton
.
Người giữ lớp Singleton
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
Ưu điểm:
-
Khởi tạo lười biếng.
-
An toàn chủ đề.
-
Hiệu suất cao trong môi trường đa luồng.
-
Để hoạt động chính xác, cần đảm bảo rằng đối tượng lớp
Singleton
được khởi tạo không có lỗi. Nếu không, lệnh gọi phương thức đầu tiêngetInstance
sẽ kết thúc bằng lỗiExceptionInInitializerError
và tất cả các lệnh gọi tiếp theo sẽ thất bạiNoClassDefFoundError
.
Thực hiện | Khởi tạo lười biếng | An toàn chủ đề | Tốc độ đa luồng | Khi nào nên sử dụng? |
---|---|---|---|---|
Giải pháp đơn giản | - | + | Nhanh | Không bao giờ. Hoặc khi việc khởi tạo lười biếng không quan trọng. Nhưng không bao giờ tốt hơn. |
Khởi tạo lười biếng | + | - | Không áp dụng | Luôn luôn khi không cần đa luồng |
Trình truy cập được đồng bộ hóa | + | + | Chậm | Không bao giờ. Hoặc khi tốc độ làm việc với đa luồng không thành vấn đề. Nhưng không bao giờ tốt hơn |
Khóa kiểm tra kép | + | + | Nhanh | Trong những trường hợp hiếm hoi khi bạn cần xử lý các trường hợp ngoại lệ khi tạo một singleton. (khi Class Holder Singleton không được áp dụng) |
Người giữ lớp Singleton | + | + | Nhanh | Luôn luôn khi cần đa luồng và có sự đảm bảo rằng một đối tượng lớp đơn sẽ được tạo mà không gặp vấn đề gì. |
Ưu và nhược điểm của mẫu Singleton
Nói chung, singleton thực hiện chính xác những gì được mong đợi ở nó:-
Cung cấp sự đảm bảo rằng một lớp sẽ chỉ có một thể hiện của lớp đó.
-
Cung cấp một điểm truy cập toàn cầu cho một thể hiện của lớp này.
-
Singleton vi phạm SRP (Nguyên tắc trách nhiệm duy nhất) - lớp Singleton ngoài trách nhiệm trước mắt còn kiểm soát số lượng bản sao của nó.
-
Sự phụ thuộc của một lớp hoặc phương thức thông thường vào một singleton không được hiển thị trong hợp đồng công khai của lớp.
-
Biến toàn cầu là xấu. Singleton cuối cùng biến thành một biến toàn cục khổng lồ.
-
Sự hiện diện của một singleton làm giảm khả năng kiểm thử của ứng dụng nói chung và các lớp sử dụng singleton nói riêng.
GO TO FULL VERSION