2013-06-18 28 views
7

Xin chào bên dưới là đoạn trích từ Effective Java 2nd Edition. Ở đây tác giả tuyên bố đoạn mã sau đây nhanh hơn 25% so với trong đó u không sử dụng biến kết quả. Theo sách "Biến này là gì để đảm bảo rằng trường chỉ đọc một lần trong trường hợp phổ biến khi nó được khởi tạo." . Tôi không thể hiểu tại sao mã này sẽ nhanh sau khi giá trị được khởi tạo so với nếu chúng tôi không sử dụng kết quả Biến cục bộ. Trong cả hai trường hợp, bạn sẽ chỉ đọc một biến động sau khi khởi tạo cho dù bạn sử dụng kết quả biến cục bộ hay không.Tại sao đôi khi kiểm tra khóa là nhanh hơn 25% trong ví dụ Java hiệu quả Joshua Bloch

// Double-check idiom for lazy initialization of instance fields 
private volatile FieldType field; 

FieldType getField() { 
    FieldType result = field; 
    if (result == null) { // First check (no locking) 
     synchronized(this) { 
      result = field; 
      if (result == null) // Second check (with locking) 
       field = result = computeFieldValue(); 
     } 
    } 
    return result; 
} 
+1

Uhm, đây có phải là ấn bản đầu tiên không? Khóa đã kiểm tra đôi đã bị nản chí trong một số năm nay là – fge

+0

Và lý do là: http://stackoverflow.com/questions/4926681/why-is-double-checked-locking-broken-in-java?rq=1 – Lenymm

+4

@fge : Không hẳn. Kể từ Java 5, nó thực sự là một mẫu OK: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html –

Trả lời

6

Khi field đã được khởi tạo, mã này là một trong hai:

if (field == null) {...} 
return field; 

hay:

result = field; 
if (result == null) {...} 
return result; 

Trong trường hợp đầu tiên bạn đọc biến dễ bay hơi hai lần trong khi trong lần thứ hai bạn chỉ đọc nó một lần. Mặc dù đọc dễ bay hơi rất nhanh, chúng có thể chậm hơn một chút so với đọc từ một biến cục bộ (tôi không biết nếu nó là 25%).

Ghi chú:

  • dễ bay hơi lần đọc là rẻ như bình thường đọc trên vi xử lý gần đây (ít nhất là x86)/JVM, ví dụ: không có sự khác biệt.
  • tuy nhiên trình biên dịch có thể tối ưu hóa tốt hơn mã mà không dễ bay hơi để bạn có thể nhận được hiệu quả từ mã được biên dịch tốt hơn.
  • 25% của một vài nano giây vẫn không nhiều.
  • nó là một thành ngữ tiêu chuẩn mà bạn có thể tìm thấy trong nhiều lớp của gói java.util.concurrent - xem ví dụ this method in ThreadPoolExecutor (có rất nhiều trong số họ)
+0

ok vì vậy nó có nghĩa là "trường trả về" cũng tạo thành một biến đọc (trường được cung cấp là dễ bay hơi) – veritas

+0

@veritas Có nó không - nếu bạn kiểm tra bytecode, bạn sẽ thấy rằng nó được nạp trước khi được trả về. – assylias

+0

ok cảm ơn rất nhiều @assylias – veritas

0

Nếu không sử dụng một biến địa phương, trong hầu hết các lời gọi chúng tôi có hiệu quả

if(field!=null) // true 
    return field; 

vì vậy có hai lần đọc dễ bay hơi, chậm hơn một lần đọc dễ bay hơi.

Thực tế JVM có thể kết hợp hai biến đọc dễ bay hơi thành một lần đọc dễ bay hơi và vẫn tuân theo JMM. Nhưng chúng tôi hy vọng JVM thực hiện một niềm tin tốt dễ bay hơi đọc mỗi khi nó được nói, không phải là một smartass và cố gắng để tối ưu hóa đi bất kỳ đọc dễ bay hơi. Hãy xem xét mã này

volatile boolean ready; 

do{}while(!ready); // busy wait 

chúng tôi mong đợi JVM thực sự tải biến nhiều lần.

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