2009-11-22 35 views
14

Trong Java, phép gán là nguyên tử nếu kích thước của biến nhỏ hơn hoặc bằng 32 bit nhưng không phải là nếu lớn hơn 32 bit.Dễ bay hơi hoặc đồng bộ cho loại nguyên thủy?

Điều gì (dễ bay hơi/đồng bộ hóa) sẽ hiệu quả hơn khi sử dụng trong trường hợp phân bổ kép hoặc dài?

Giống như,

volatile double x = y; 

đồng bộ không áp dụng với lập luận nguyên thủy. Làm cách nào để sử dụng đồng bộ hóa trong trường hợp này? Tất nhiên tôi không muốn khóa lớp của tôi, vì vậy không nên sử dụng this.

+0

Tại sao bạn "không muốn khóa [lớp] của bạn"? (Lưu ý rằng khóa trên một phương thức cá thể chỉ được lấy ra trên chính đối tượng đó). Cũng lưu ý rằng JVM hiện đại là cực kỳ tiên tiến và chi phí hoạt động của đồng bộ hóa thường không phải là vấn đề –

+0

Tôi có một jsp nơi nhiều người truy cập/cập nhật dữ liệu. dữ liệu nằm trong một đối tượng bộ nhớ cache và ở đó tôi cần những đồng bộ hóa và et al vì số lượng người có thể rất nhiều. – DKSRathore

+1

Tôi nghĩ tôi nên đi với AomicDouble. – DKSRathore

Trả lời

5

Nếu bạn tìm thấy khóa trên đối tượng chính nó quá nặng, sau đó đồng bộ là cách để đi. Trước khi Java 1.5 dễ bay hơi có thể là một lựa chọn tốt, nhưng bây giờ dễ bay hơi có thể có tác động rất lớn bằng cách buộc lệnh đặt hàng trên phương thức mà nhiệm vụ xảy ra. Tạo một đối tượng riêng biệt (private final Object X_LOCK = new Object();) và đồng bộ hóa trên nó khi thiết lập hoặc nhận được giá trị của đôi đó. Điều này sẽ cung cấp cho bạn một mức độ kiểm soát tốt về khóa, mà có vẻ như bạn cần.

Trong gói đồng thời mới có nhiều tùy chọn hơn, chẳng hạn như AtomicReference có thể là một thay thế tốt cho dễ bay hơi nếu bạn thực sự cần tránh đồng bộ hóa.

+1

Bạn đang nói 'biến động' là ít hiệu quả hơn? – Pacerier

+0

@Pacerier, nó thực sự phụ thuộc (@oxbow_lakes đạt hiệu suất điểm khá tốt), nhưng nó có tác động lớn hơn bây giờ (với 1,5 và lớn hơn) so với nó được sử dụng để, và nó có thể được tối giản hơn. Trong thực tế, điều này sẽ phải là một số mã được sử dụng khá nhiều với rất nhiều tranh chấp và cơ hội tốt để hướng dẫn sắp xếp lại vấn đề. Vì vậy, nó không phải là một tối ưu hóa hiệu suất đáng giá mà không có bằng chứng đó là một vấn đề trong trường hợp cụ thể của bạn (có thực sự rất ít những thứ được). – Yishai

+1

Đồng bộ hóa trên một đối tượng có cùng một điều xảy ra trước khi đặt hàng ngữ nghĩa. – eckes

26

Bạn đang cố gắng làm gì? Các từ khóa synchronizedvolatile là các cơ chế trong Java có thể được sử dụng để đảm bảo rằng các giá trị nhất quán được quan sát bởi các luồng khác nhau đọc cùng một dữ liệu. Cụ thể là chúng cho phép bạn giải thích về mối quan hệ xảy ra trước quan hệ trong chương trình của bạn.

Bạn chỉ đơn giản là không thể tránh sử dụng một trong số volatile hoặc synchronized để truy cập đúng các trường không phải final trong chương trình nhiều luồng. Điều đó nói rằng, lý do chính khiến bạn có thể yêu cầu synchronized hơn volatile là yêu cầu sử dụng nguyên tử so sánh và đặt hoạt động (tức là nó sẽ không được xem xét hiệu suất). Ví dụ, trong một đa luồng chương trình:

volatile int i = 0; 

public void foo() { 
    if (i == 0) i = i + 1; 
} 

Đoạn mã trên vốn đã không an toàn, mặc dù tuyên bố của biến như là phương tiện dễ bay hơi mà đọc và viết được đỏ mặt vào bộ nhớ chính - việc thực hiện an toàn duy nhất của như một phương pháp sẽ là một cái gì đó như:

int i = 0; 

public synchronized void foo() { 
    if (i == 0) i = i + 1; 
} 

Vậy bạn nên chọn cái nào? Vâng, nếu bạn có nhiều chủ đề sửa đổi một trường phụ thuộc vào giá trị của trường đó (tức là so sánh và đặt), thì synchronized là giải pháp an toàn duy nhất.

Cũng đáng nói: chi phí hoạt động của synchronized không phải là vấn đề (trong phần lớn các trường hợp). Các vấn đề về hiệu năng đồng bộ hóa thường là do tắc nghẽn mã không cần thiết, các khóa chết hoặc các khóa sinh động và có thể được giảm nhẹ nếu cần thiết. Bất kỳ tinh khiết đồng hồ chu kỳ overhead sẽ lấn át bởi những thứ khác bạn ứng dụng nào: file IO, truy vấn cơ sở dữ liệu, truy cập từ xa, vv

+3

Dễ bay hơi không an toàn ngay cả đối với 'i ++' nếu' i' là 'double' hoặc' long '. – abyx

+0

Có, nhưng tôi nên chọn cái nào trong những trường hợp như vậy. – DKSRathore

+0

Điểm của tôi là về so sánh và đặt; 'if (i == 0) i ++' không an toàn, ngay cả khi tôi là 'int' –

2

KDSRathore, bạn có thể sử dụng một số ổ khóa rõ ràng, hoặc làm cho một số đối tượng Object giả = new Object (), trên đó bạn đồng bộ hóa trong setter/getter của đôi đó

+1

Bạn nên sử dụng các trình bao bọc nguyên tử hiện có cho các nguyên thủy hơn là tự triển khai chúng. – abyx

+0

Trường hợp nào nói rằng các trình bao bọc nguyên tử hiện có được ưu tiên cho một khóa chuyên dụng? Một khóa chuyên dụng có thể tốt hơn vì phạm vi khóa bị hạn chế. –

3

dễ bay hơi chắc chắn là con đường để đi nếu bạn chỉ làm bài tập. Tôi chắc chắn bạn biết, nhưng vì nó đã được đưa ra: nếu bạn muốn làm các hoạt động phức tạp hơn (tăng giá trị ví dụ), bạn sẽ cần phải syncrhonize. i ++ không bao giờ là chủ đề an toàn cho bất kỳ kiểu biến nào. Bạn cần phải đồng bộ. i + + và những thứ tương tự vì nó thực sự là hơn 1 thao tác.

Không: Nó được bày tỏ rằng bạn có thể sử dụng AtomicDouble nhưng hiện tại không có AtomicDouble trong java.util.concurrent.atomic

Nếu bạn đang làm một bội số hoạt động trên x, đòi hỏi phải cài đặt nó vào một giá trị mới cuối cùng, nó có thể làm điều này trong một thread an toàn cách mà không có khóa những gì như vậy bao giờ hết, và có nó được thread an toàn, bằng cách sử dụng so sánh và thiết lập. Ví dụ:

AtomicLong x = new AtomicLong(SomeValue); 
public void doStuff() { 
    double oldX; 
    double newX; 
    do { 
    oldX = x.get(); 
    newX = calculateNewX(oldX); 
    } while (!x.compareAndSet 
     (Double.doubleToLongBits(oldX), Double.doubleToLongBits(newX))); 

Điều này làm việc vì compareAndSet sẽ xem liệu giá trị của x có thay đổi kể từ lần cuối bạn đọc nó hay không. Nếu x đã thay đổi thì bạn buộc phải thực hiện tính toán lại và thử cài đặt lại.

Tất nhiên, bạn có thể triển khai AtomicDouble của riêng mình thay vì thực hiện các chuyển đổi doubleToLongBits này. Hãy xem AtomicFieldUpdater.

+0

Cảm ơn bạn đã trả lời tốt. Vâng, tôi đã nhìn không có AtomicDouble và vì vậy tôi đã kết thúc với việc khóa một đối tượng giả và thực hiện nhiệm vụ kép của mình. Theo bạn dễ bay hơi là tốt. Nhưng điều này đúng trong trường hợp tăng gấp đôi? – DKSRathore

+1

Thực tế là AtomicLong định nghĩa giá trị của nó là dễ bay hơi và định nghĩa của AtomicLong.set đơn giản là "value = newValue". Tôi sẽ coi đó là bằng chứng tốt cho thấy biến số biến động 64 bit có các phép gán nguyên tử như java 1.6. –

+0

Đó là một ví dụ được viết tốt với ý định bất thường: nó quay cho đến khi nó cuối cùng có thể ghi đè lên giá trị 'x'. Điển hình hơn là tính toán một giá trị cập nhật, cố gắng lưu trữ nó, và, khi thất bại, bỏ cuộc, giả sử rằng một số luồng khác đã có trong đó và nâng cao tình hình trước. Hợp đồng trong ví dụ của bạn là mỗi chuỗi cạnh tranh sẽ tăng giá trị một lần, làm bất cứ điều gì cần thiết khi đối mặt với tranh chấp. – seh

0

Theo the oracle documentation, bạn có thể sử dụng dễ bay hơi để đề cập đến đối tượng đúp:

volatile Double x = y; 

"viết thư cho và đọc tài liệu tham khảo luôn nguyên tử, bất kể họ đang thực hiện như 32-bit hoặc 64 giá trị bit. "

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