2011-10-03 34 views
16

Tôi đang tạo một chương trình bằng java đua một vài chiếc xe với nhau. Mỗi chiếc xe là một sợi riêng biệt.Trình điều khiển có tăng cường an toàn cho thread?

Khi xe ô tô hoàn thành cuộc đua, mỗi xe đều gọi phương thức này. Tôi đã thử nghiệm phương pháp ở tốc độ hẹn giờ khác nhau, và nó có vẻ hoạt động tốt. Nhưng tôi nhận ra rằng mỗi thread đang truy cập vào biến carsComplete, đôi khi tại cùng một thời điểm chính xác (ít nhất là ở phạm vi ngày lệnh cho tôi).

Vì vậy, câu hỏi của tôi là: phương pháp này có an toàn không?

public static String completeRace() 
{ 
     Date accessDate = new Date(); 
     System.out.println("Cars Complete: " + carsComplete + " Accessed at " + accessDate.toString()); 
     switch(++carsComplete) 
     { 
      case 1: return "1st"; 
      case 2: return "2nd"; 
      case 3: return "3rd"; 
      default: return carsComplete + "th";  
     } 
} 

Trả lời

19

Không, bạn nên sử dụng một cái gì đó như java.util.concurrent.atomic.AtomicInteger. Nhìn vào phương pháp getAndIncrement() của nó.

+3

Hoặc đồng bộ hóa thứ gì đó trước khi tăng. –

+1

@Ted: Tôi đặt cược rằng việc đồng bộ hóa trên một cái gì đó đắt hơn. –

+1

@Eric - Tôi sẽ ngạc nhiên nếu AtomicInteger không thực hiện một số đồng bộ hóa tại một thời điểm nào đó (có thể trong mã gốc). Nó sẽ là thú vị để chuẩn. Từ nguồn cho AtomicInteger, nó dường như phụ thuộc vào một thuật toán so sánh và thiết lập nguyên tử mà thử lại cho đến khi nó phát hiện không có vấn đề gì. Không có trường hợp xấu nhất bị ràng buộc cho điều này; trong các kịch bản có độ chủ đề cao, tôi nghĩ nó có thể rất chậm vào những thời điểm ngẫu nhiên. –

5

++ toán tử không phải là nguyên tử. Nhìn vào đây http://madbean.com/2003/mb2003-44/. Đối với các hoạt động nguyên tử bạn có thể sử dụng AtomicInteger

AtomicInteger atomicInteger = new java.util.concurrent.atomic.AtomicInteger(0) 

và mỗi khi bạn muốn tăng bạn có thể gọi atomicInteger.incrementAndGet() phương pháp mà trả về một int nguyên thủy. 0 là giá trị ban đầu mặc định cho số nguyên nguyên tử.

+0

tôi vẫn không nhận được lý do tại sao hoạt động nguyên tử là thread an toàn? điều gì sẽ xảy ra khi hai luồng thực thi CAS cùng một lúc? nó không phải là một điều kiện chủng tộc ?? – hardik

+1

@Hardik không có điều kiện chủng tộc vì mối quan hệ 'xảy ra trước đó' (http://en.wikipedia.org/wiki/Happened-before) được thực thi bởi các hoạt động nguyên tử và do đó kết quả là an toàn luồng. –

8

Pre-increment trên intkhông chủ đề an toàn, sử dụng AtomicInteger đó là lock-free:

AtomicInteger carsComplete = new AtomicInteger(); 

//... 

switch(carsComplete.incrementAndGet()) 

BTW mã dưới đây là không an toàn chủ đề là tốt. Bạn có thể nói lý do tại sao?

carsComplete.incrementAndGet(); 
switch(carsComplete.get()) 
+1

Bởi vì luồng 1 có thể đạt đến câu lệnh chuyển đổi, hãy chuyển đến luồng 2, chạy incrementAndGet do đó tăng lưu trữ sao lưu cho carsComplete, tại điểm đó thread 1 thực hiện lại với cửa hàng sao lưu đã được tăng gấp đôi, một lần theo từng luồng. –

+0

Chính xác, tuy nhiên không có giải thưởng lần này ;-). –

5

Tương tự như trong C++, toán tử ++ không phải là nguyên tử. Nó thực sự là nhiều hơn 1 lệnh đang được thực hiện dưới mui xe (không bị lừa bởi chỉ nhìn thấy một đơn giản ++i; nó là load/add/store) và vì có nhiều hơn 1 lệnh liên quan mà không đồng bộ, bạn có thể có nhiều sự xen kẽ khác nhau kết quả sai.

Nếu bạn cần incrent các carsComplete một cách thread-safe bạn có thể sử dụng cấu trúc java của AtomicInteger hoặc bạn có thể đồng bộ hóa toàn bộ phương pháp

1

Câu hỏi là “là tăng tiền nhà khai thác chủ đề an toàn?”

Ans : Không, tại sao? vì số lượng hướng dẫn có liên quan. Nguyên tử có nghĩa là hoạt động đơn lẻ, ở đây hoạt động tải/thêm/lưu trữ cần được thực hiện. Vì vậy, không phải là một hoạt động nguyên tử.

Same for post increment. 
Các vấn đề liên quan