2012-10-12 26 views
34

Với lớp sau đây:giá trị trở lại của toán tử gán trong mã đồng thời

class Foo { 
    public volatile int number; 

    public int method1() { 
    int ret = number = 1; 
    return ret; 
    } 

    public int method2() { 
    int ret = number = 2; 
    return ret; 
    } 
} 

và được nhiều chủ đề kêu gọi method1()method2() đồng thời trên cùng một Foo Ví dụ, có thể một cuộc gọi đến method1() bao giờ quay trở lại bất cứ điều gì khác hơn 1?

Trả lời

10

Các JLS 15,26 quy định cụ thể:

Có 12 nhà khai thác chuyển nhượng; tất cả đều liên kết cú pháp đúng (chúng nhóm từ phải sang trái). Như vậy, a = b = c nghĩa là a = (b = c), gán giá trị của c cho b và sau đó gán giá trị của b cho a.

Câu trả lời của Ted Hopp cho thấy javac của Sun không tuân theo hành vi này, có thể là tối ưu hóa.

Do luồng ở đây, hành vi của phương thức 1 sẽ không xác định. Nếu trình biên dịch của Sun làm cho hành vi không đổi thì nó không bị phá vỡ khỏi hành vi không xác định.

+0

Tối ưu hóa này có thể được cho phép đối với các trường [trường dễ bay hơi] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4) không? – trashgod

+0

Tôi tò mò mặc dù, nếu đó là những gì spec dự định. Tôi nghĩ rằng họ có thể đã chỉ định b trong "và sau đó gán giá trị của b cho một" chỉ để làm cho nó rõ ràng trong trường hợp a, b và c có các loại khác nhau (nói nguyên thủy tích phân/nổi loại) và chuyển đổi xảy ra, chuyển nhượng xảy ra như-nếu giá trị chuyển đổi được sử dụng. Ví dụ: nếu a và c là đôi và b là một phao. – BeeOnRope

+0

@BeeOnRope Điều đó khá khả thi - trang JLS có vẻ quan tâm hơn đến chuyển đổi loại so với các trường dễ bay hơi. – Bringer128

15

Tôi nghĩ câu trả lời phụ thuộc vào trình biên dịch. Ngôn ngữ specifies:

Tại thời gian chạy, kết quả của biểu thức gán là giá trị của biến sau khi bài tập đã diễn ra.

Tôi cho rằng về mặt lý thuyết giá trị có thể thay đổi trước khi chuyển nhượng thứ hai (ngoài cùng bên trái) xảy ra.

Tuy nhiên, với trình biên dịch javac của Sun, method1 sẽ sẽ biến thành:

0: aload_0 
1: iconst_1 
2: dup_x1 
3: putfield  #2; //Field number:I 
6: istore_1 
7: iload_1 
8: ireturn 

này bản sao hằng 1 trên stack và tải nó vào number và sau đó vào ret trước khi trở ret. Trong trường hợp này, sẽ không quan trọng nếu giá trị được lưu trữ trong number được sửa đổi trước khi gán cho ret, vì 1, không được gán number.

+0

Ngôn ngữ gây nhầm lẫn vì dường như ngụ ý rằng "giá trị" của biến được sử dụng, dường như ngụ ý rằng lần đọc thứ hai là – BeeOnRope

+0

Bytecode chắc chắn là thú vị, nhưng không chứng minh cái gì là _required_ bởi spec. – BeeOnRope

+0

@BeeOnRope - Trên bề mặt, nó xuất hiện với tôi rằng mã được tạo ra bởi trình biên dịch không hoàn toàn phù hợp với đặc tả ngôn ngữ. Tuy nhiên, có thể là "sau khi chuyển nhượng đã xảy ra" có nghĩa là "ngay lập tức sau" và rằng bất kỳ thay đổi nào khác đối với biến từ các chủ đề khác không ảnh hưởng đến kết quả.), sau đó javac tạo mã đúng, cách điện giá trị từ các thay đổi đồng thời sang 'số'. –

5

Tuyên bố có chứa đọc dễ bay hơi hoặc không chứa đọc dễ bay hơi. Không thể có bất kỳ sự mơ hồ nào ở đây, vì đọc dễ bay hơi là rất quan trọng đối với ngữ nghĩa chương trình.

Nếu javac có thể được tin cậy, chúng tôi có thể kết luận rằng tuyên bố không liên quan đến việc đọc dễ bay hơi của number. Giá trị của biểu thức gán x=y thực sự chỉ là giá trị của y (sau khi chuyển đổi).

Chúng tôi cũng có thể suy ra rằng

System.out.println(number=1); 

không liên quan đến đọc number

String s; 

    (s="hello").length(); 

không liên quan đến đọc s

x_1=x_2=...x_n=v 

không liên quan đến đọc x_n, x_n-1, ...; thay vào đó, giá trị của v được gán trực tiếp cho x_i (sau khi chuyển đổi cần thiết thông qua các loại x_n, ... x_i

+0

Tôi hy vọng bạn là đúng, nhưng spec chắc chắn không cung cấp cho nhiều trọng lượng để giải thích đó, phải không? Tôi nghĩ rằng dễ bay hơi là một cá trích đỏ. Nó có thể chứa một đọc không dễ bay hơi, và vẫn làm những điều kỳ lạ với luồng (dễ bay hơi có nghĩa là một số điều phải xảy ra, nhưng những thứ tương tự * có thể * xảy ra với một đọc bình thường). – BeeOnRope

+0

Câu trả lời này đúng với tôi trong thực tế.JLS không chỉ rõ rằng 'a = b = c' tương đương với' b = c; a = b; 'nhưng chúng dường như đã thực hiện nó như là' b = c; a = c; 'để ngăn chặn sự cần thiết cho việc truy xuất trường dễ bay hơi chậm. Như đã nói, tham chiếu JLS được trích dẫn có thể chỉ là một ví dụ kém chứ không phải là * trình biên dịch phải thực hiện tham chiếu * này. – Bringer128

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