2013-04-03 34 views
19

Tôi đã thực hiện một số nghiên cứu về người độc thân, đặc biệt liên quan đến việc khởi tạo lười biếng và háo hức của những người độc thân.Mẫu Singleton với sự kết hợp giữa tải trọng lười biếng và an toàn chủ đề

Một ví dụ về khởi háo hức:

public class Singleton 
{ 
    //initialzed during class loading 
    private static final Singleton INSTANCE = new Singleton(); 

    //to prevent creating another instance of Singleton 
    private Singleton(){} 

    public static Singleton getSingleton(){ 
     return INSTANCE; 
    } 
} 

nhưng như trình bày ở trên rằng đó là háo hức khởi và an toàn thread trái sang JVM nhưng bây giờ, tôi muốn có cùng một khuôn mẫu này nhưng với khởi tạo lười biếng.

vì vậy tôi đưa ra cách tiếp cận này:

public final class Foo { 
    private static class FooLoader { 
     private static final Foo INSTANCE = new Foo(); 
    } 
    private Foo() { 
     if (FooLoader.INSTANCE != null) { 
      throw new IllegalStateException("Already instantiated"); 
     } 
    } 
    public static Foo getInstance() { 
     return FooLoader.INSTANCE; 
    } 
} 

Như đã trình bày ở trên Kể từ khi dòng

private static final Foo INSTANCE = new Foo(); 

chỉ được thực hiện khi FooLoader lớp được thực tế sử dụng, điều này sẽ chăm sóc của các instantiation lười biếng , và nó có được đảm bảo là luồng an toàn không.

Điều này có đúng không?

+0

Duplicate http://stackoverflow.com/q/5842273/823393 – OldCurmudgeon

+1

Đây thực sự không phải là khởi tạo lười biếng? Ví dụ có thể được nạp trước phương thức 'getInstance' được gọi. – Perception

+0

Điều này chắc chắn là lười biếng. 'INSTANCE' chỉ được khởi tạo trên tham chiếu đầu tiên của lớp. Mà sẽ chỉ được tham chiếu trong lời gọi đầu tiên của 'getInstance'. Mặc dù tôi sẽ loại bỏ việc tạo kiểm tra trong constructor. –

Trả lời

17

Đoạn mã thứ hai của bạn, theo ý kiến ​​của tôi, cách tốt nhất để tạo luồng đơn giản là an toàn cho một singleton. Tên thực tế có tên mẫu

Initialization-on-demand holder idiom

Tôi khuyên bạn nên sử dụng nó.

+0

Cảm ơn rất nhiều. đi qua bài viết hoàn hảo explnation –

+0

Trả lời về hành vi tải lớp của JVM thay vì làm cho sự lười biếng của bạn rõ ràng âm thanh với tôi như một ý tưởng tồi. Có lẽ thậm chí không có lợi ích hiệu suất so với cách đơn giản. – einpoklum

+0

@einpoklum Tôi thấy ví dụ của bạn được sử dụng DCL + dễ bay hơi. Mặc dù nó là chính xác (wrt tuyên bố nó dễ bay hơi) Tôi thà sử dụng ví dụ sau đó của mình sau đó DCL. Tôi thấy nó dễ đọc hơn và tiết kiệm hơn. Cuối cùng ví dụ của bạn là sai, bạn nên đồng bộ hóa trên 'Foo.class' và không phải là cá thể. –

1

Cách tốt nhất là thực sự sử dụng Enum Way:

public enum Singleton { 
    INSTANCE; 
    public void execute (String arg) { 
      //... perform operation here ... 
    } 
} 
+1

Tôi có thể nói đó là cách tồi tệ nhất ... và Bloch nên hoàn lại tiền cho người đọc của mình :) – ZhongYu

+0

Xin chào, tôi biết: anh chàng xấu ... –

+0

+1 zhong.j.yu Tôi cũng không bao giờ thích kiểu này mặc dù Bloch đã nói về nó rất cao và sau đó mọi người khác theo dõi nó:/ –

2

Điều thứ hai là rất xấu về khả năng đọc, thứ nhất là phù hợp. Hãy xem cái này article. Khóa kiểm tra kép của nó, nhưng cũng sẽ cung cấp cho bạn thông tin rộng về đa luồng đơn.

+0

Làm thế nào là thứ hai * rất xấu *? –

+0

Nó sẽ làm việc, nhưng tôi không thấy bất kỳ sử dụng trong FooLoader và nếu tuyên bố bên trong constructor. Hãy để nó không phải là xấu nhưng quá tải. Hãy nhớ rằng những người khác sẽ đọc mã của bạn. Vì vậy, hãy giữ nó đơn giản. – Mikhail

+0

Tôi đồng ý, không cần kiểm tra trong hàm tạo và cần phải xóa. Nhưng khi nói về thread-safety tôi thấy hoàn toàn không có gì sai với đoạn mã thứ hai. –

5

Thiết kế đầu tiên của bạn thực sự là lười. Hãy nghĩ về nó, cá thể chỉ được tạo khi lớp được khởi tạo; lớp chỉ được khởi tạo khi phương thức getSingleton() được gọi là [1]. Vì vậy, cá thể chỉ được tạo khi được yêu cầu, tức là nó được tạo ra một cách uể oải.

[1] http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

+0

+1 bạn đã đúng, nó vẫn còn lười biếng mặc dù tôi tưởng tượng anh ta sẽ có các phương thức khác có sẵn trong đó một tham chiếu tĩnh khác sẽ tạo ra nó. –

+0

Mặc dù không có một lớp đơn chứa các phương thức tĩnh (trừ 'getInstance()') – ZhongYu

+2

nhưng không chắc là nó chứa trường tĩnh công cộng :) –

0

Theo tôi, đây là mô hình không phù hợp để sử dụng. Nó làm cho các giả định về hành vi của JVM không tầm thường và khó hiểu. Ngoài ra, nó có một lớp giả. Các lớp giả nên tránh khi có thể.

Tôi đề nghị cách tiếp cận đơn giản:

public class Foo { 
    private volatile static final Foo instance = null; 

    private Foo() { 
    } 

    public static Foo instance() { 
     if (instance == null) instance = new Foo(); 
     return instance; 
     } 
    } 
} 

... mặc dù, điều này không làm việc như nó vốn có - nó không đề an toàn .. Những gì bạn thực sự muốn là mô hình double-kiểm tra được trình bày tại khoản 71 của Bloch's Hiệu quả Java; xem here.Thích ứng với ví dụ tại liên kết đến trường hợp của bạn, chúng tôi nhận được:

public class Foo { 
    private volatile static final Foo instance = null; 

    private Foo() { 
    } 

    public static Foo instance() { 
     if (instance != null) return instance; 
     synchronized(instance) { 
      Foo result = instance; 
      if (instance == null) { 
       result = instance = new Foo(); 
      return result; 
     } 
    } 
} 

Ghi chú:

  • Đừng lo lắng về hiệu suất của mã này, các JVM hiện đại chăm sóc nó và nó chỉ là tốt. Xét cho cùng, premature optimization is the root of all evil.
  • Như được đề xuất trong các câu trả lời khác, ở trên không phải là giải pháp ưa thích của Bloch, nhưng tôi nghĩ việc sử dụng enum cho singleton là không phù hợp ngữ nghĩa giống như những gì OP ban đầu.
+1

Nếu bạn không lo lắng về hiệu suất, chỉ cần sử dụng ví dụ đầu tiên của bạn và đồng bộ hóa toàn bộ phương pháp. Thậm chí đơn giản hơn! –

+1

Đối với các phiên bản thay thế của khóa kép, hãy xem https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java –

Các vấn đề liên quan