CodeGym /Blog Java /Ngẫu nhiên /Lớp đơn Java

Lớp đơn Java

Xuất bản trong nhóm
CHÀO! Hôm nay chúng ta sẽ đi sâu vào chi tiết của các mẫu thiết kế khác nhau, bắt đầu với mẫu Java Singleton. Hãy xem lại: chúng ta biết gì về các mẫu thiết kế nói chung? Các mẫu thiết kế là những phương pháp hay nhất mà chúng ta có thể áp dụng để giải quyết một số vấn đề đã biết. Các mẫu thiết kế thường không bị ràng buộc với bất kỳ ngôn ngữ lập trình nào. Hãy nghĩ về chúng như một tập hợp các đề xuất giúp bạn tránh những sai lầm và tránh phát minh lại bánh xe.Mẫu thiết kế: Singleton - 1

Singleton trong Java là gì?

Singleton là một trong những mẫu thiết kế cấp lớp đơn giản nhất. Đôi khi người ta nói "lớp này là singleton", có 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 trong đó chúng ta hạn chế việc khởi tạo đối với 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 một đối tượng duy nhất. cơ sở dữ liệu. Mẫu thiết kế singleton mô tả cách chúng ta có thể đạt được điều này. Singleton là một mẫu thiết kế thực hiện hai việc:
  1. Nó đảm bảo rằng sẽ chỉ có một thể hiện của lớp.

  2. Nó cung cấp một điểm truy cập toàn cầu duy nhất cho trường hợp đó.

Do đó, có hai tính năng đặc trưng cho hầu hết mọi triển khai của mẫu đơn:
  1. Một nhà xây dựng tư nhân. Điều này hạn chế khả năng tạo các đối tượng của lớp bên ngoài chính lớp đó.

  2. Một phương thức tĩnh công khai trả về 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 đối với thể hiện của lớp.

Tùy chọn thực hiện

Mẫu thiết kế singleton được áp 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ó. Như mọi khi, không có lựa chọn hoàn hảo nào ở đây, nhưng chúng ta nên cố gắng đạt được một lựa chọn. Trước hết, hãy quyết định điều gì tạo nên điều tốt và điều xấu, và những chỉ số nào ảnh hưởng đến cách chúng ta đánh giá các triển khai khác nhau của mẫu thiết kế. Hãy bắt đầu với những điều tốt đẹp. Dưới đây là các yếu tố làm cho việc triển khai trở nên hấp dẫn và hấp dẫn hơn:
  • Khởi tạo chậm: thể hiện không được tạo cho đến khi cần thiết.

  • Mã đơn giản và minh bạch: số liệu này, tất nhiên, là chủ quan, nhưng nó 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: ít hoặc không chặn luồng khi chia sẻ tài nguyên.

Bây giờ là khuyết điểm. Chúng tôi sẽ liệt kê các yếu tố khiến việc triển khai gặp khó khăn:
  • Không khởi tạo lười biếng: khi lớp được tải khi ứng dụng bắt đầu, bất kể có cần thiết hay không (nghịch lý thay, trong thế giới CNTT, tốt hơn là nên lười biếng)

  • Mã phức tạp và khó đọc. Số liệu này cũng mang tính chủ quan. Nếu mắt bạn bắt đầu chảy máu, chúng tôi sẽ cho rằng việc triển khai không phải là tốt nhất.

  • Thiếu an toàn chủ đề. Nói cách khác, "luồng nguy hiểm". 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 chặn lẫn nhau mọi lúc hoặc thường xuyên khi chia sẻ tài nguyên.

Mã số

Bây giờ chúng ta đã sẵn sàng xem xét các tùy chọn triển khai khác nhau và chỉ ra những ưu và nhược điểm:

Đơ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:
  • Mã đơn giản và minh bạch

  • Chỉ an toàn

  • Hiệu suất cao trong môi trường đa luồng

Nhược điểm:
  • Không khởi tạo lười biếng.
Trong nỗ lực khắc phục thiếu sót trước đó, chúng tôi nhận được triển khai số hai:

Khởi tạo lười biếng


public class Singleton {
  private static final 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.

Nhược điểm:
  • Chủ đề không an toàn

Việc thực hiện này là thú vị. Chúng tôi có thể khởi tạo một cách lười biếng, nhưng chúng tôi đã mất đi sự an toàn của luồng. Đừng lo lắng — chúng tôi đồng bộ hóa mọi thứ trong triển khai số ba.

Truy cập được đồng bộ hóa


public class Singleton {
  private static final 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.

  • Chỉ an toàn

Nhược điểm:
  • Hiệu suất đa luồng kém

Xuất sắc! Trong triển khai số ba, chúng tôi khôi phục sự an toàn của luồng! Tất nhiên, nó chậm... Bây giờ phương thức getInstance đã được đồng bộ hóa, vì vậy nó chỉ có thể được thực thi bởi một luồng tại một thời điểm. Thay vì đồng bộ hóa toàn bộ phương thức, chúng ta thực sự chỉ cần đồng bộ hóa phần khởi tạo thể hiện mới của nó. Nhưng chúng ta không thể đơn giản sử dụng một khối được đồng bộ hóa để bọc phần chịu trách nhiệm tạo phiên bản mới. Làm như vậy sẽ không đảm bảo an toàn cho luồng. Tất cả phức tạp hơn một chút. Đồng bộ hóa thích hợp có thể được nhìn thấy dưới đây:

Kiểm tra khóa hai lần


public class Singleton {
    private static final 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.

  • Chỉ an toàn

  • Hiệu suất cao trong môi trường đa luồng

Nhược điểm:
  • Không được hỗ trợ trong các phiên bản Java trước đó dưới 1.5 (việc sử dụng từ khóa dễ bay hơi đã được sửa kể từ phiên bản 1.5)

Lưu ý rằng để tùy chọn triển khai này hoạt động chính xác, một trong hai điều kiện phải được thỏa mãn. Biến 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à chủ sở hữu lớp singleton .

chủ lớp


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.

  • Chủ đề an toàn.

  • Hiệu suất cao trong môi trường đa luồng.

Nhược điểm:
  • Hoạt động chính xác yêu cầu đảm bảo rằng đối tượng đơn lẻ được khởi tạo mà không có lỗi. Mặt khác, lệnh gọi đầu tiên đến phương thức getInstance sẽ dẫn đến ExceptionInInitializerError và tất cả các lệnh gọi tiếp theo sẽ tạo ra NoClassDefFoundError .

Việc triển khai này gần như hoàn hảo. Đó là lười biếng, và chủ đề an toàn, và nhanh chóng. Nhưng nó có một sắc thái, như được giải thích trong danh sách nhược điểm. So sánh các triển khai khác nhau của mẫu đơn:
Thực hiện Khởi tạo lười biếng Chỉ an toàn hiệu suất đa luồng Khi nào thì sử dụng?
Đơn giản - + Nhanh Không bao giờ. Hoặc có thể khi khởi tạo lười biếng không quan trọng. Nhưng không bao giờ sẽ 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
Truy cập được đồng bộ hóa + + Chậm Không bao giờ. Hoặc có thể khi hiệu suất đa luồng không thành vấn đề. Nhưng không bao giờ sẽ tốt hơn.
Kiểm tra khóa hai lần + + Nhanh Trong một số trường hợp hiếm hoi khi bạn cần xử lý các ngoại lệ khi tạo singleton (khi singleton của chủ sở hữu lớp không được áp dụng)
chủ lớp + + Nhanh Bất cứ khi nào cần xử lý đa luồng và có sự đảm bảo rằng đối tượng singleton sẽ được tạo mà không gặp sự cố.

Ưu và nhược điểm của mẫu đơn

Nói chung, một singleton thực hiện chính xác những gì được mong đợi ở nó:
  1. Nó đảm bảo rằng sẽ chỉ có một thể hiện của lớp.

  2. Nó cung cấp một điểm truy cập toàn cầu duy nhất cho trường hợp đó.

Tuy nhiên, mô hình này có nhược điểm:
  1. Một singleton vi phạm nguyên tắc trách nhiệm duy nhất: ngoài các nhiệm vụ trực tiếp của nó, lớp singleton còn kiểm soát số lượng các phiên bản.

  2. Sự phụ thuộc của một lớp bình thường vào một singleton không được hiển thị trong hợp đồng công khai của lớp.

  3. Các biến toàn cầu là xấu. Cuối cùng, một singleton biến thành một biến toàn cầu khổng lồ.

  4. Sự hiện diện của một singleton làm giảm khả năng kiểm tra của ứng dụng nói chung và các lớp sử dụng singleton nói riêng.

Và thế là xong! :) Chúng tôi đã cùng bạn khám phá Java Singleton Class. Bây giờ, trong phần còn lại của cuộc đời bạn, khi trò chuyện với những người bạn lập trình viên của mình, bạn không chỉ có thể đề cập đến việc mẫu đó tốt như thế nào mà còn có thể nói một vài lời về điều gì khiến nó trở nên tồi tệ. Chúc bạn nắm vững kiến ​​thức mới này.

Đọc thêm:

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION