2015-11-12 10 views
7

Khi làm khởi lười biếng của một singleton tĩnh trong Java bạn có thể làm điều này:Chúng ta có thể tải xuống không đau của một thành viên Java theo cách tương tự như cách chúng ta có thể với các trình đơn tĩnh không?

public class Bob { 

    private static class SingletonWrapper { 
     private static final Bob instance = new Bob(); 
    } 

    public static Bob getInstance() { 
     return SingletonWrapper.instance; 
    } 

} 

Bởi vì lớp bên trong SingletonWrapper chỉ nạp khi lần đầu tiên truy cập vào Bob() không được tạo ra cho đến khi getInstance() được gọi.

Câu hỏi của tôi là liệu có bất kỳ thủ thuật tương tự nào có thể được sử dụng để thực hiện việc khởi tạo lười biếng của biến thành viên trong ngữ cảnh không tĩnh hay không.

public class Bob { 

    // Clearly this doesn't work as not lazy 
    private final InnerWrapper wrapper = new InnerWrapper(); 

    private class InnerWrapper { 
     private final Jane jane = new Jane(); 
    } 

    public Jane getJane() { 
     return wrapper.jane; 
    } 

} 

Có cách nào chúng ta có thể có một thể hiện của Jane trong Bob và sợi một cách an toàn có trường hợp duy nhất được tạo ra theo yêu cầu mà không sử dụng đôi khóa séc hoặc AtomicReference. Lý tưởng nhất là phương thức get nên đơn giản như trong ví dụ này, nhưng nếu điều đó là không thể thì việc thực hiện đơn giản và nhanh nhất có thể (hiệu quả nhất) của phương thức get là lý tưởng.

+0

@assylias Khi bình luận nói rằng sẽ không làm khởi lười biếng. Ngay sau khi bạn tạo một 'Bob' bạn cũng luôn luôn và ngay lập tức tạo ra một' Jane'. –

+0

Tội nghiệp của tôi tôi đọc quá nhanh - bất kỳ lý do nào bạn không muốn có một AtomicReference? Nó có thể sẽ hoạt động tốt hơn tùy chọn CHM. – assylias

+0

@assylias Không có lý do cụ thể. Về cơ bản, tôi chỉ muốn biết những lựa chọn của tôi là gì - xem xét thủ thuật wrapper tĩnh đơn giản và hiệu quả như thế nào cho việc khởi tạo lười biếng của những người độc thân đó là một sự xấu hổ, chúng ta không thể nghĩ ra điều gì đó tương tự cho việc khởi tạo lười biếng của các thành viên, –

Trả lời

8

Không, không có quy tắc đồng bộ hóa cho các loại instantiating như có để khởi tạo các lớp học. Bạn phải tự thêm chúng. Cho dù bạn làm điều đó với đôi kiểm tra khóa hoặc một số cơ chế khác là vào bạn.

Bắt đầu với Java 8, tôi thích sử dụng ConcurrentHashMap#computeIfAbsent để đạt được sự lười biếng.

class Bob { 
    private final ConcurrentHashMap<String, Jane> instance = new ConcurrentHashMap<>(1); 

    public Jane getJane() { 
     return instance.computeIfAbsent("KEY", k -> new Jane()); // use whatever constant key 
    } 
} 

Ngoài ra còn có these solutions để khởi tạo lười không có ràng buộc an toàn chỉ. Tôi không thể điều chỉnh chúng một cách rõ ràng cho một ngữ cảnh đa luồng.

Như đã nêu trong các nhận xét, khóa được kiểm tra kép sẽ luôn nhanh hơn các giải pháp này vì chúng không bao gồm tất cả các lông tơ để ẩn cài đặt.

+0

Trong khi đó là mã sạch hiệu suất so sánh như thế nào? –

+0

Việc kiểm tra khóa kép có thể nhanh nhất vì không có kiểm tra thêm. Bạn đang làm hầu hết những gì bạn cần làm để có được ví dụ. –

+1

Cách tốt hơn so với giải pháp tiết của tôi bằng cách sử dụng ổi, mặc dù logic là tương tự –

2

Bạn có thể sử dụng bộ nhớ cache ổi của:

public class Bob { 
    private final static Object KEY = new Object(); 

    private final Cache<Object, Jane> cache = 
     CacheBuilder.newBuilder() 
        .build(new CacheLoader<Object, Jane>() { 
           @Override 
           public Jane load() { 
            return new Jane(); 
           } 
          }); 

    public Jane getJane() { 
     return cache.get(KEY); 
    } 

} 
+0

Đây không phải là trường hợp sử dụng chính của bộ nhớ cache: tự động loại bỏ. Một concurrentHashMap sẽ thực hiện công việc và hoạt động tốt hơn bộ nhớ đệm ổi – Zava

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