2012-01-07 16 views
19

Đây là về heo đất dễ bay hơi. Mục đích: Tôi muốn đạt được một chiếc vars nhẹ có tầm nhìn. Tính nhất quán của a_b_c không quan trọng. Tôi có một loạt các vars và tôi không muốn làm cho chúng trở nên dễ bay hơi.Con heo đất dễ bay hơi. Điều này có đủ cho sự hiển thị không?

Mã này có an toàn không?

class A { 
    public int a, b, c; 
    volatile int sync; 

    public void setup() { 
     a = 2; 
     b = 3; 
     c = 4; 
    } 

    public void sync() { 
     sync++; 
    } 
} 

final static A aaa = new A(); 

Thread0: 
aaa.setup(); 
end 

Thread1: 
for(;;) {aaa.sync(); logic with aaa.a, aaa.b, aaa.c} 

Thread2: 
for(;;) {aaa.sync(); logic with aaa.a, aaa.b, aaa.c} 
+0

Nếu tính nhất quán không quan trọng, tại sao bạn quan tâm liệu các biến có được đồng bộ hóa không? – cHao

+0

Tôi không quan tâm đến tính nhất quán nhưng tôi quan tâm đến sự hiển thị. – temper

+0

Sau đó, chỉ cần đặt chúng ở chế độ công khai? Nhưng sau đó tại sao gắn thẻ câu hỏi với đồng bộ hóa? –

Trả lời

26

Java Memory Model xác định xảy ra-trước mối quan hệ trong đó có các thuộc tính sau (giữa những người khác):

  • "mỗi hành động trong một thread xảy ra-trước mỗi hành động trong đó chủ đề đó đến sau theo thứ tự chương trình" (chương trình quy định theo thứ tự)
  • "Một ghi vào một lĩnh vực không ổn định xảy ra-trước mỗi đọc tiếp theo của cùng dễ bay hơi" (quy tắc biến dễ bay hơi)

Hai thuộc tính cùng với transitivity của xảy ra-trước mối quan hệ bao hàm những bảo đảm tầm nhìn đó OP tìm kiếm theo cách sau đây:

  1. một ghi vào a trong chủ đề 1 xảy ra-trước một ghi vào sync trong một cuộc gọi đến sync() trong chủ đề 1 (quy tắc trật tự chương trình).
  2. Các ghi vào sync trong cuộc gọi đến sync() trong chủ đề 1 xảy ra-trước đọc để sync trong một cuộc gọi đến sync trong chủ đề 2 (dễ bay hơi quy tắc biến).
  3. Đọc từ sync trong cuộc gọi đến sync() trong chủ đề 2 xảy ra trước đọc từ a trong chuỗi 2 (quy tắc thứ tự chương trình).

Điều này ngụ ý rằng câu trả lời cho câu hỏi là có, ví dụ: cuộc gọi đến sync() trong mỗi lần lặp trong bài 1 và 2 đảm bảo tầm nhìn của những thay đổi để a, bc để thread khác (s). Lưu ý rằng điều này đảm bảo chỉ hiển thị. Không có bảo đảm loại trừ lẫn nhau tồn tại và do đó tất cả các bất biến ràng buộc a, bc có thể bị vi phạm.

Xem thêm Java theory and practice: Fixing the Java Memory Model, Part 2. Đặc biệt phần "đảm bảo mới cho ổn định" mà nói

Theo mô hình bộ nhớ mới, khi thread ghi vào một biến động biến V, và thread B đọc từ V, bất kỳ giá trị biến là có thể nhìn thấy A tại thời điểm V được viết được đảm bảo ngay bây giờ là hiển thị cho B.

+2

@PhilippWendler Hai quy tắc ngụ ý hiển thị các thay đổi đối với 'a',' b' và 'c' được tạo trong chuỗi 1 đến chuỗi 2 theo cách sau: thay đổi thành' a' trong chuỗi 1 _happens-before_ a write to 'sync 'trong một cuộc gọi tiếp theo tới' sync() 'trong chủ đề 1 (quy tắc 1) mà _happens-before_ đọc từ' sync' trong một cuộc gọi đến 'sync' trong chuỗi 2 (quy tắc 2) mà _happens-before_ đọc từ' a' trong chuỗi 2 (một lần nữa, quy tắc 1). –

+1

@PhilippWendler Xem thêm bài viết tôi liên kết, đặc biệt là phần "Đảm bảo mới cho dễ bay hơi". Một báo giá liên quan hơn: "Theo mô hình bộ nhớ mới, khi luồng A ghi vào biến biến động V và chuỗi B đọc từ V, bất kỳ giá trị biến nào được hiển thị cho A tại thời điểm V được viết được đảm bảo ngay bây giờ để hiển thị B. " –

+0

Bạn có thể giải thích' thay đổi thành một trong chuỗi 1 xảy ra trước khi ghi để đồng bộ trong một cuộc gọi tiếp theo để đồng bộ hóa() trong chuỗi 1 (quy tắc 1) '. Những gì tôi thấy là '{aaa.sync(); logic với aaa.a, aaa.b, aaa.c} 'so' sync() 'được gọi trước khi truy cập vào' a'. Cảm ơn –

0

Bạn không thực sự phải đồng bộ hóa theo cách thủ công, chỉ cần sử dụng cấu trúc dữ liệu được đồng bộ hóa tự động, như java.util.concurrent.atomic.AtomicInteger.

Hoặc bạn có thể thực hiện phương thức sync()synchronized.

+1

Tôi không quan tâm đến một biến đồng bộ ở tất cả – temper

3

Tăng giá trị giữa các chuỗi không bao giờ an toàn chỉ với chỉ volatile. Điều này chỉ đảm bảo rằng mỗi thread nhận được một giá trị cập nhật, không phải là gia số là nguyên tử, bởi vì ở cấp độ assembler, ++ của bạn thực sự là một vài lệnh có thể xen kẽ.

Bạn nên sử dụng AtomicInteger để tăng nhanh nguyên tử.

Chỉnh sửa: Đọc lại những gì bạn cần thực sự là hàng rào bộ nhớ. Java không có hướng dẫn hàng rào bộ nhớ, nhưng bạn có thể sử dụng khóa cho hàng rào bộ nhớ "tác dụng phụ". Trong trường hợp đó tuyên bố phương pháp đồng bộ hóa đồng bộ để giới thiệu một hàng rào ngầm:

void synchronized sync() { 
    sync++; 
} 
+1

đồng bộ ++ chỉ là một mẹo để đọc và viết o một bộ nhớ dễ bay hơi .. – temper

+0

@temper: Tôi nhận ra điều đó. Chỉnh sửa là câu trả lời cho câu hỏi của bạn. – Tudor

1

Từ javadoc:

Một unlock (khối đồng bộ hoặc phương pháp thoát) của một màn hình xảy ra-trước khi khóa mỗi tiếp theo (khối đồng bộ hoặc phương pháp mục nhập) của cùng một màn hình đó. Và bởi vì mối quan hệ xảy ra trước đó là chuyển tiếp, tất cả các hành động của một luồng trước khi mở khóa xảy ra trước khi tất cả các hành động tiếp theo với bất kỳ luồng nào khóa màn hình .

Việc ghi vào trường dễ bay hơi xảy ra trước mỗi lần đọc tiếp theo của cùng trường đó. Việc ghi và đọc các trường dễ bay hơi có hiệu ứng tương tự như bộ nhớ khi vào và thoát khỏi màn hình, nhưng làm không yêu cầu khóa loại trừ lẫn nhau.

Vì vậy, tôi nghĩ rằng văn bản cho var dễ bay hơi không phải là một tương đương với syncronization trong trường hợp này và nó không đảm bảo xảy ra-trước khi đặt hàng và tầm nhìn của những thay đổi trong thread1 để thread2

1

Mẫu thường như thế này.

public void setup() { 
    a = 2; 
    b = 3; 
    c = 4; 
    sync(); 
} 

Tuy nhiên, trong khi điều này đảm bảo các chủ đề khác sẽ thấy thay đổi này, các chủ đề khác có thể thấy thay đổi không hoàn chỉnh. ví dụ. Thread2 có thể thấy a = 2, b = 3, c = 0 hoặc thậm chí có thể là a = 2, b = 0, c = 4;

Sử dụng tính năng đồng bộ hóa() trên đầu đọc không giúp ích gì nhiều.

+0

Tôi có hiểu lầm. trong thread1 Tôi gọi phương thức thiết lập. sau khi nó trong thread2 tôi có thể thấy b = 0? – gstackoverflow

+0

@gstackoverflow Bạn cần hai phần. Một rào cản viết ở cuối bên viết và một rào cản đọc lúc bắt đầu ở phía đọc. Điều này đảm bảo bạn đọc những gì được viết trong một chủ đề khác. Viết cho một volatile liên quan đến một hàng rào viết, đọc từ một volatile liên quan đến một rào cản đọc. –

+1

@PeterLawrey, Nhưng trong câu hỏi ông * đã * có một rào cản đọc phải không? Có phải 'aaa.sync()' trong Thread 1 và Thread 2 được coi là đủ? – Pacerier

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