2010-09-03 6 views
14

hiện đơn giản an toàn chỉ đơn giản là không dễ hiểu với mọi bộ mã hóa, vì vậy tôi muốn tạo một lớp trong khung công tác của chúng tôi.Mẫu để tạo sự đơn giản an toàn cho thread đơn giản trong java

Bạn nghĩ sao về điều này? Bạn có thấy cái gì xấu về nó? Có cái gì đó tương tự như trong Apache Commons? Làm thế nào tôi có thể làm cho nó tốt hơn?

Supplier.java

public interface Supplier<T> { 
    public T get(); 
} 

LazyThreadSafeInstantiator.java

public class LazyThreadSafeInstantiator<T> implements Supplier<T> { 
    private final Supplier<T> instanceSupplier; 

    private volatile T obj; 

    public LazyThreadSafeInstantiator(Supplier<T> instanceSupplier) { 
     this.instanceSupplier = instanceSupplier; 
    } 

    @Override 
    // http://en.wikipedia.org/wiki/Double-checked_locking 
    public T get() { 
     T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won't hurt. 
     if (result == null) { 
      synchronized(this) { 
       result = obj; 
       if (result == null) { 
        result = instanceSupplier.get(); 
        obj = result; 
       } 
      } 
     } 
     return result; 
    } 
} 

sử dụng Ví dụ:

public class Singleton1 { 
    private static final Supplier<Singleton1> instanceHolder = 
     new LazyThreadSafeInstantiator<Singleton1>(new Supplier<Singleton1>() { 
      @Override 
      public Singleton1 get() { 
       return new Singleton1(); 
      } 
     }); 

    public Singleton1 instance() { 
     return instanceHolder.get(); 
    } 

    private Singleton1() { 
     System.out.println("Singleton1 instantiated"); 
    } 
} 

Cảm ơn

+7

Vấn đề lớn nhất của tôi là với chính mẫu đó. Từ Wikipedia: Một số xem xét nó là một anti-pattern, đánh giá rằng nó bị lạm dụng, giới thiệu những hạn chế không cần thiết trong các tình huống mà một trường hợp duy nhất của một lớp là không thực sự cần thiết, và giới thiệu trạng thái toàn cầu vào một ứng dụng. Cân nhắc sử dụng tính năng tiêm phụ thuộc thay thế. –

+3

Tôi rất muốn đề nghị tránh một cái gì đó như thế này. Sử dụng khung DI thay thế. –

+0

có thể trùng lặp của [mẫu Singleton trong java- lười biếng Intialization] (http://stackoverflow.com/questions/2521895/singleton-pattern-in-java-lazy-intialization) –

Trả lời

54

thread-safe lười biếng singleton instantion là kinda không dễ dàng để hiểu để mỗi coder

Không, nó thực sự rất, rất dễ dàng:

public class Singleton{ 
    private final static Singleton instance = new Singleton(); 
    private Singleton(){ ... } 
    public static Singleton getInstance(){ return instance; } 
} 

Hơn thế nữa, làm cho it a enum:

public enum Singleton{ 
    INSTANCE; 
    private Singleton(){ ... } 
} 

Đó là luồng an toàn, và nó lười (khởi tạo xảy ra ở thời gian tải lớp, và Java không tải các lớp cho đến khi chúng được giới thiệu lần đầu).

Sự thật là 99% thời gian bạn không cần tải chậm ở tất cả. Và trong số 1% còn lại, trong 0,9% ở trên là hoàn toàn lười biếng đủ.

Bạn đã chạy một hồ sơ và xác định rằng ứng dụng của bạn belings đến 0,01% mà thực sự cần lười biếng-tải-tại-đầu tiên truy cập? Không nghĩ vậy. Vậy thì tại sao bạn lại lãng phí thời gian của mình để pha trộn những sự trừng phạt mã Rube Goldbergesque để giải quyết một vấn đề không tồn tại?

+0

cảm ơn tất cả các bạn vì câu trả lời. Tôi nghĩ tôi hoàn toàn hiểu lầm những gì tôi muốn đạt được. –

+0

Tất cả các bạn đã giúp tôi nhắm mục tiêu vấn đề thực sự, mà tôi đã đăng tại http://stackoverflow.com/questions/3636244/thread-safe-cache-of-one-object-in-java –

+0

Trong 'lớp' mẫu 'cuối cùng static' bị thiếu. Tôi đã sửa nó. Tôi muốn thêm liên kết tới bài đăng này từ câu hỏi khác. –

3

Không phải là hai lần kiểm tra khóa mô hình và sử dụng dễ bay hơi broken trên các trình biên dịch JIT và các hệ thống đa lõi/bộ xử lý do Mô hình Bộ nhớ Java & có khả năng xảy ra việc thực hiện lệnh không?

Nói chung, có vẻ như một khuôn khổ cho các trình đơn là quá mức cần thiết cho những gì cơ bản là một mẫu khá đơn giản để triển khai chính xác.

+1

Tôi đã được ấn tượng rằng họ đã cố định trong java 1.5 –

+0

@ mR_fr0g, không. Một phần của vấn đề với anti-pattern này là nó cố gắng đọc tham chiếu mà không cần khóa.Đồng bộ hóa thích hợp yêu cầu các khóa trên * tất cả * đọc * và * ghi để các chủ đề được đảm bảo xem các cập nhật từ các chủ đề khác –

+0

Bạn có thông tin khá cũ. Java 1.5 ra mắt vào cuối năm 2004, và điều này đã được sửa chữa kể từ đó (miễn là trường được đánh dấu là "dễ bay hơi") Trên thực tế, Java 1.5 đã quá cũ đến nỗi Sun ngừng hỗ trợ nó vào tháng 11 năm ngoái. –

5

Đối với phiên bản dễ đọc hơn (theo ý kiến ​​của tôi) so với phiên bản được trình bày trong câu hỏi, có thể tham khảo Initialization on Demand Holder idiom, được giới thiệu bởi Bill Pugh. Nó không chỉ an toàn khi xem xét mô hình bộ nhớ Java 5, singleton cũng được khởi tạo một cách lười biếng.

3

Trông quá mức với tôi.

Tôi thực sự không thấy cách có trợ giúp lớp giúp.

Trước hết, nó sử dụng khóa đôi thành ngữ và nó đã được chứng minh một lần và bị hỏng.

Thứ hai, nếu bạn CÓ ĐỂ sử dụng singleton, tại sao không khởi tạo static final ví dụ.

public class Singleton1 { 
    private static final Singleton1 instanceHolder = 
     new Singletong1(); 

    public Singleton1 instance() { 
     return instanceHolder; 
    } 

    private Singleton1() { 
     System.out.println("Singleton1 instantiated"); 
    } 
} 

Mã này an toàn chỉ và đã được chứng minh là hoạt động.

Kiểm tra câu trả lời của Vineet Reynolds khi bạn cần khởi tạo bản sao đơn lẻ trên đầu tiên nhận được. Trong nhiều trường hợp, tôi nghĩ rằng cách tiếp cận đó cũng là quá mức cần thiết.

0

Tôi đồng ý với các áp phích khác và nói rằng điều này có vẻ như quá mức cần thiết, nhưng đã nói rằng tôi nghĩ rằng đây là điều mà một nhà phát triển cơ sở có thể bị sai. Tôi nghĩ rằng bởi vì hành vi của nhà cung cấp xây dựng singleton (được hiển thị bên dưới) sẽ giống nhau trong hầu hết các trường hợp, tôi sẽ bị cám dỗ để đặt điều này là hành vi mặc định trong LazyThreadSafeInstantiator. Việc sử dụng các lớp bên trong annonomous mỗi khi bạn muốn sử dụng một singleton thực sự là lộn xộn.

 @Override 
     public Singleton1 get() { 
      return new Singleton1(); 
     } 

Điều này có thể được thực hiện bằng cách cung cấp công cụ xây dựng quá tải đưa Class vào singleton cần thiết.

public class LazyThreadSafeInstantiator<T> implements Supplier<T> { 
    private final Supplier<T> instanceSupplier; 

    private Class<T> toConstruct; 

    private volatile T obj; 

    public LazyThreadSafeInstantiator(Supplier<T> instanceSupplier) { 
     this.instanceSupplier = instanceSupplier; 
    } 

    public LazyThreadSafeInstantiator(Class<t> toConstruct) { 
     this.toConstruct = toConstruct; 
    } 

    @Override 
    // http://en.wikipedia.org/wiki/Double-checked_locking 
    public T get() { 
     T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won't hurt. 
     if (result == null) { 
      synchronized(this) { 
       result = obj; 
       if (result == null) { 
        if (instanceSupplier == null) { 
         try { 
         Constructor[] c = toConstruct.getDeclaredConstructors(); 
         c[0].setAccessible(true); 
         result = c[0].newInstance(new Object[] {}); 
         } catch (Exception e) { 
         //handle 
         } 
         result = 
        } else { 
         result = instanceSupplier.get(); 
        } 
        obj = result; 
       } 
      } 
     } 
     return result; 
    } 
} 

Điều này sau đó sẽ được sử dụng như vậy.

private static final Supplier<Singleton1> instanceHolder = 
    new LazyThreadSafeInstantiator<Singleton1>(Singleton1.getClass()); 

Đây là ý kiến ​​của tôi là sạch hơn một chút. Bạn có thể alos mở rộng này hơn nữa để sử dụng các đối số constructor.

+1

Ý tưởng hay, nhưng bạn phải làm cho các hằng số công khai để có được nó chạy. –

+0

Vâng Điểm tốt. Bạn có thể sử dụng sự phản chiếu để truy cập hàm tạo và làm cho nó có thể truy cập được. Nó sẽ là một chút hacky mặc dù, vì vậy nếu bạn làm điều đó chắc chắn rằng bạn tài liệu địa ngục ra khỏi nó. –

0
Lazy<X> lazyX= new Lazy<X>(){ 
    protected X create(){ 
     return new X(); 
    }}; 

X x = lazyX.get(); 

abstract public class Lazy<T> 
{ 
    abstract protected T create(); 

    static class FinalRef<S> 
    { 
     final S value; 
     FinalRef(S value){ this.value =value; } 
    } 

    FinalRef<T> ref = null; 

    public T get() 
    { 
     FinalRef<T> result = ref; 
     if(result==null) 
     { 
      synchronized(this) 
      { 
       if(ref==null) 
        ref = new FinalRef<T>(create()); 
       result = ref; 
      } 
     } 
     return result.value; 
    } 
} 

trừ có lẽ là get đầu tiên() trong một chủ đề, tất cả get() các cuộc gọi không cần đồng bộ hoặc không ổn định đọc. mục tiêu ban đầu của khóa kiểm tra kép là đạt được.

+0

quá mức cần thiết đối với tôi. không phải là cách đơn giản nhất. – ses