2009-10-19 22 views
16

Tôi đã nghe về điều này xảy ra trong mã không an toàn không an toàn do không đúng cách xây dựng các đối tượng nhưng tôi thực sự không có khái niệm, ngay cả sau khi đọc trong cuốn sách của Goetz. Tôi muốn củng cố sự hiểu biết của tôi về mã này mùi như tôi có thể làm điều đó và thậm chí không nhận ra nó. Vui lòng cung cấp mã trong lời giải thích của bạn để làm cho nó gắn bó, cảm ơn.Cách "này" thoát khỏi hàm tạo trong Java?

Trả lời

15

Thực sự đơn giản ví dụ:

public class Test 
{ 
    private static Test lastCreatedInstance; 

    public Test() 
    { 
     lastCreatedInstance = this; 
    } 
} 
+1

Điều này không giống như một cái gì đó mà bình thường sẽ xảy ra mặc dù, ngay cả tôi sẽ không nghĩ đến việc viết này. Nhưng phần trốn thoát rõ ràng với tôi. Điều gì sẽ là hậu quả của mã này? –

+0

@non sequitor: Hãy tưởng tượng nếu có rất nhiều khởi tạo khác trong hàm tạo ... nhưng một luồng khác sử dụng 'lastCreatedInstance' trước khi hàm tạo đã kết thúc. Nó có thể sử dụng một đối tượng được khởi tạo một nửa. –

21

Ví dụ: trong hàm tạo, bạn tạo trình xử lý sự kiện lớp bên trong (nó có tham chiếu ngầm định đối tượng hiện tại) và đăng ký nó vào danh sách người nghe.
=> Vì vậy, đối tượng của bạn có thể được sử dụng bởi một luồng khác, mặc dù nó không hoàn thành việc thực thi hàm tạo của nó.

 public class A { 

     private boolean isIt; 
     private String yesItIs; 

     public A() { 
     EventListener el = new EventListener() { ....}; 
     StaticListeners.register(el); 
     isIt = true; 
     yesItIs = "yesItIs"; 
     } 
    } 

Một vấn đề nữa có thể xảy ra sau này: đối tượng A có thể được tạo ra hoàn toàn, được cung cấp cho tất cả các chủ đề, sử dụng bởi thread khác ... ngoại trừ việc chủ đề đó có thể nhìn thấy một ví dụ như tạo , yesItIs với giá trị "yesItIs", nhưng không phải là isIt! Tin hay không, điều này có thể xảy ra! Điều gì xảy ra là:

=>đồng bộ hóa chỉ bằng một nửa về chủ đề chặn, nửa còn lại là hiển thị liên luồng.

Lý do cho rằng sự lựa chọn Java là hiệu suất: tầm nhìn liên thread sẽ giết hiệu suất nếu tất cả các dữ liệu sẽ được chia sẻ với tất cả các chủ đề, vì vậy dữ liệu chỉ được đồng bộ hóa được đảm bảo để được chia sẻ ...

+0

Nếu bạn đang biến đổi một tĩnh, sau đó bạn đã có vấn đề. Một trường hợp thực tế hơn là nơi bạn thêm một tham chiếu (ví dụ, thông qua một người nghe) vào một đối số được truyền cho hàm tạo. –

+0

@Tom Tôi đồng ý, thay vì đăng ký với StaticListeners, tôi có thể đăng ký vào cùng một đối tượng được truyền qua hàm tạo. Điều này thường được cho là thích hợp hơn, nhưng chúng ta có thể thấy nó hoàn hảo :-) Về nó là 'thực tế', mileage của bạn có thể khác nhau, nhưng tôi đoán rằng đối với nhiều dự án sử dụng globals xảy ra (cho dù tĩnh, hoặc ThreadLocal, hoặc truy cập thông qua một toàn cầu khác) ... Tôi tìm thấy nó ngắn hơn để xây dựng các ví dụ ngắn ;-) – KLE

5

Đây là lý do tại sao khóa kiểm tra kép không hoạt động. Mã ngây thơ

if(obj == null) 
{ 
    synchronized(something) 
    { 
    if (obj == null) obj = BuildObject(...); 
    } 
} 
// do something with obj 

là không an toàn vì sự phân công vào biến cục bộ có thể xảy ra trước khi phần còn lại của việc xây dựng (constructor hoặc phương pháp nhà máy). Do đó, luồng 1 có thể ở bước BuildObject, khi chuỗi 2 đi vào cùng một khối, phát hiện một số không obj và sau đó tiếp tục hoạt động trên một đối tượng không đầy đủ (chuỗi 1 đã được lên lịch ở giữa cuộc gọi).

+0

Bạn có thể giải thích việc bỏ phiếu xuống không. Khóa kiểm tra kép là ví dụ điển hình về việc thoát tham chiếu "này". –

+0

Chỉ có ví dụ này không phải vì 'obj' không bao giờ được gán cho bất cứ điều gì cho đến khi' BuildObject' đã hoàn thành hàm tạo của nó. – Bombe

+2

Đây là nơi bạn sai - nhiệm vụ có thể xảy ra trước khi hoàn thành xây dựng. Xem http://www.ibm.com/developerworks/java/library/j-dcl.html để có một cuộc thảo luận đầy đủ. –

4
public class MyClass{ 
    String name;  

    public MyClass(String s) 
    { 
     if(s==null) 
     { 
      throw new IllegalArgumentException(); 
     } 
     OtherClass.method(this); 
     name= s; 
    } 

    public getName(){ return name; } 
} 

Trong đoạn mã trên, OtherClass.method() được truyền một thể hiện của MyClass đó là vào thời điểm đó không đầy đủ xây dựng, ví dụ: chưa hoàn thành các hợp đồng mà các name tài sản là phi null.

2

Steve Gilham là chính xác trong việc xác định lý do tại sao đôi kiểm tra khóa bị hỏng. Nếu luồng A đi vào phương thức đó và obj là null, luồng đó sẽ bắt đầu tạo một thể hiện của đối tượng và gán nó cho obj. Chủ đề B có thể có thể nhập trong khi luồng A vẫn khởi tạo đối tượng đó (nhưng không hoàn thành) và sau đó sẽ xem đối tượng là không rỗng nhưng trường của đối tượng đó có thể chưa được khởi tạo. Một đối tượng được xây dựng một phần.

Tuy nhiên, cùng một loại vấn đề có thể xảy ra nếu bạn cho phép từ khóa này thoát khỏi hàm tạo. Giả sử constructor của bạn tạo ra một thể hiện của một đối tượng mà một nhánh, và đối tượng đó chấp nhận kiểu đối tượng của bạn. Bây giờ đối tượng của bạn có thể chưa được khởi tạo đầy đủ, đó là một số trường của bạn có thể rỗng.Một tham chiếu đến đối tượng của bạn bằng một đối tượng mà bạn đã tạo trong hàm dựng của bạn bây giờ có thể tham chiếu đến bạn như một đối tượng không rỗng nhưng nhận các giá trị trường rỗng.

Một chút giải thích thêm:

constructor của bạn có thể khởi tạo tất cả các lĩnh vực trong lớp học của bạn, nhưng nếu bạn cho phép 'này' để thoát khỏi trước khi bất kỳ của các đối tượng khác được tạo ra, họ có thể được null (hoặc mặc định primative) khi xem bởi chủ đề khác nếu 1. Họ không tuyên bố chính thức hoặc 2. Họ không tuyên bố bất ổn

+0

Khóa kiểm tra kép không bị hỏng nữa. Nếu biến là biến động, * và mã được cấu trúc đúng, * nó hoạt động tốt dưới mô hình bộ nhớ Java 5+. Sau khi bắt đầu lười biếng, "chi phí" chỉ là một đọc dễ bay hơi. – erickson

+0

Có thêm biến động cho biến sẽ cho phép nó được khởi tạo chính xác, nhưng vì vậy sẽ khai báo nó cuối cùng. Nhưng ngay cả Goetz cũng lưu ý rằng mặc dù tuyên bố nó dễ bay hơi nhưng nó không phải là bằng chứng đầy đủ 100%. Điều gì nếu, ví dụ, đối tượng dễ bay hơi có các trường được khai báo trong hàm tạo của nó. Bạn sẽ có một đối tượng biến động myObject, được khởi tạo trong constructor. Và một luồng khác gọi myObject.getSomeField(). GetSomeOtherField(). Nếu someOtherField được khai báo trong hàm tạo của someField, nó có thể có thể là null trong khi someOtherField có thể được xây dựng một phần –

+0

Tạo một luồng trong một hàm tạo là một ví dụ tốt. –

0
public class Test extends SomeUnknownClass{ 
    public Test(){ 
     this.addListner(new SomeEventListner(){ 
      @Override 
      void act(){} 
     }); 
    } 
} 

Sau này instanse hoạt động của SomeEventListner sẽ có một liên kết để kiểm tra đối tượng, như một lớp bên trong bình thường.

Thêm ví dụ có thể được tìm thấy ở đây: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html

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