2012-06-26 34 views
6

Nói, tôi có một đối tượng dữ liệu:Chủ đề hồ bơi, dữ liệu chia sẻ, Java Synchronization

class ValueRef { double value; }

đâu mỗi đối tượng dữ liệu được lưu trữ trong một bộ sưu tập tổng thể:

Collection<ValueRef> masterList = ...;

Tôi cũng có một tập hợp các công việc, trong đó mỗi công việc có một bộ sưu tập các đối tượng dữ liệu cục bộ (trong đó mỗi đối tượng dữ liệu cũng xuất hiện trong masterList):

class Job implements Runnable { 
    Collection<ValueRef> neededValues = ...; 
    void run() { 
     double sum = 0; 
     for (ValueRef x: neededValues) sum += x; 
     System.out.println(sum); 
    } 
} 

Sử dụng hợp cụ thể:

  1. for (ValueRef x: masterList) { x.value = Math.random(); }

  2. dụng nhất một hàng đợi công việc với một số công việc.

  3. Thức dậy một hồ bơi thread

  4. Chờ cho đến khi mỗi công việc đã được đánh giá

Lưu ý: Trong quá trình đánh giá công việc, tất cả các giá trị đều không đổi. Tuy nhiên, các luồng đã có thể đánh giá các công việc trong quá khứ và giữ lại các giá trị được lưu trong bộ nhớ cache.

Câu hỏi: số tiền tối thiểu của đồng bộ hóa cần thiết để đảm bảo mỗi chuỗi thấy giá trị mới nhất là gì?

Tôi hiểu đồng bộ hóa từ màn hình/quan điểm khóa, tôi không hiểu đồng bộ hóa từ phối cảnh bộ nhớ cache (ví dụ: mô hình bộ nhớ khi nhập/thoát của khối được đồng bộ hóa). Với tôi, có vẻ như tôi cần phải đồng bộ hóa một lần trong chuỗi cập nhật các giá trị để cam kết các giá trị mới vào bộ nhớ chính và một lần cho mỗi chuỗi công nhân, để xóa bộ đệm để các giá trị mới được đọc. Nhưng tôi không chắc chắn cách tốt nhất để làm điều này.

Cách tiếp cận của tôi: tạo màn hình chung: static Object guard = new Object(); Sau đó, đồng bộ hóa trên guard, trong khi cập nhật danh sách chính. Sau đó, cuối cùng, trước khi bắt đầu hồ bơi thread, một lần cho mỗi luồng trong hồ bơi, đồng bộ hóa trên guard trong một khối trống.

Điều đó thực sự gây ra sự tuôn ra đầy đủ của bất kỳ giá trị nào được đọc bởi chuỗi đó? Hoặc chỉ các giá trị được chạm vào bên trong khối đồng bộ hóa? Trong trường hợp đó, thay vì một khối trống, có lẽ tôi nên đọc từng giá trị một lần trong một vòng lặp?

Cảm ơn thời gian của bạn.


Chỉnh sửa: Tôi nghĩ câu hỏi của tôi sẽ bị hỏng khi tôi thoát khỏi khối đồng bộ, mỗi lần đọc đầu tiên (sau thời điểm đó) có chuyển sang bộ nhớ chính không? Bất kể những gì tôi đồng bộ hóa?

+0

Dường như gần như là một nơi hoàn hảo để tận dụng từ khóa dễ bay hơi – ControlAltDel

+0

Tôi đang viết một lần (có hiệu quả liên tục), nhưng có khả năng đọc hàng triệu lần. Dễ bay hơi không bao giờ được lưu trữ cục bộ. Nếu tôi tạo ra các hồ bơi thread mỗi lần, mã sẽ làm việc tốt w/o đồng bộ/dễ bay hơi (vì không có bộ nhớ cache trước sẽ tồn tại). –

+0

Tôi không thấy nhu cầu dễ bay hơi ở đây. Nếu ValueRef có hiệu quả bất biến, chỉ cần làm cho nó thực sự bất biến. Sử dụng Double. Tạo một bộ sưu tập mới cho mỗi công việc trước khi nó được lên kế hoạch và bọc nó trong unmodifiableCollection (giống như một lời nhắc nhở). Bạn có vấn đề gì? –

Trả lời

3

Không quan trọng là các luồng của một nhóm luồng đã đánh giá một số công việc trong quá khứ.

Javadoc của Executor nói: hiệu ứng

Memory nhất quán: Hoạt động trong một thread trước khi nộp một đối tượng Runnable để một Executor xảy ra-trước khi thực hiện nó bắt đầu, có lẽ trong thread khác.

Vì vậy, miễn là bạn sử dụng triển khai pool chuẩn và thay đổi dữ liệu trước khi gửi công việc bạn không nên lo lắng về hiệu ứng hiển thị bộ nhớ.

+0

Điều này là do ... trong các chủ đề công nhân, có một khối đồng bộ đang chờ công việc mới không? Và khi khối đó thoát, toàn bộ bộ nhớ cache của toàn bộ chuỗi sẽ bị xóa? Tôi có thể chỉ đồng bộ hóa trên một cái gì đó ngẫu nhiên và có được tác dụng tương tự? –

+0

@AndrewRaffensperger: Nó không quan trọng như thế nào nó được thực hiện - có một sự đảm bảo và nó sẽ được cung cấp. Về câu hỏi cuối cùng - về cơ bản như vậy, nhưng không có ý nghĩa: không có phương tiện đồng bộ hóa bổ sung, bạn không thể nói rằng các khối được đồng bộ hóa trong các chuỗi công việc được thực hiện sau khi khối đồng bộ trong chuỗi chính; với phương tiện bổ sung của đồng bộ hóa nó là dư thừa. – axtavt

2

Điều bạn đang lập kế hoạch cho âm thanh là đủ. Nó phụ thuộc vào cách bạn có kế hoạch "thức dậy hồ bơi thread".

Mô hình bộ nhớ Java cung cấp tất cả các ghi được thực hiện bởi một luồng trước khi nhập khối synchronized hiển thị cho các chuỗi mà sau đó đồng bộ hóa trên khóa đó. Vì vậy, nếu bạn chắc chắn rằng các chuỗi công nhân bị chặn trong một cuộc gọi wait() (phải nằm trong khối synchronized) trong thời gian bạn cập nhật danh sách chính, khi họ thức dậy và trở thành runnable, các sửa đổi được thực hiện bởi chủ đề chính sẽ được hiển thị cho các chủ đề này.

Tôi sẽ khuyến khích bạn, tuy nhiên, để áp dụng các tiện ích đồng thời cấp cao hơn trong gói java.util.concurrent. Những điều này sẽ mạnh mẽ hơn giải pháp của riêng bạn, và là một nơi tốt để học đồng thời trước khi đi sâu hơn.


Chỉ cần làm rõ: Đó là hầu như không thể kiểm soát đề người lao động mà không cần sử dụng một khối đồng bộ nơi một tấm séc được thực hiện để xem liệu người lao động có một nhiệm vụ để thực hiện. Do đó, bất kỳ thay đổi nào được thực hiện bởi chuỗi bộ điều khiển cho công việc xảy ra trước khi chuỗi công nhân tỉnh dậy. Bạn yêu cầu một khối synchronized hoặc ít nhất một biến số volatile hoạt động như một rào cản bộ nhớ; Tuy nhiên, tôi không thể nghĩ làm thế nào bạn muốn tạo một hồ bơi thread với bằng cách sử dụng một trong những.

Như một ví dụ về những ưu điểm của việc sử dụng các gói java.util.concurrency, hãy xem xét điều này: bạn có thể sử dụng một khối synchronized với một cuộc gọi wait() trong nó, hoặc một vòng lặp bận rộn-chờ đợi với một biến volatile. Bởi vì các chi phí của bối cảnh chuyển đổi giữa các chủ đề, một chờ đợi bận rộn thực sự có thể thực hiện tốt hơn trong điều kiện nhất định — nó không cần thiết ý tưởng khủng khiếp mà người ta có thể giả định ở cái nhìn đầu tiên.

Nếu bạn sử dụng tiện ích Đồng thời (trong trường hợp này, có thể là ExecutorService), lựa chọn tốt nhất cho trường hợp cụ thể của bạn có thể được thực hiện cho bạn, bao thanh toán trong môi trường, bản chất nhiệm vụ và nhu cầu của các chủ đề khác thời gian cho đi. Đạt được mức độ tối ưu hóa đó là rất nhiều công việc không cần thiết.

+0

Tôi không thể đủ khả năng chi phí của java.util.concurrent. Dữ liệu trong ví dụ của tôi được cập nhật một lần, sau đó trở thành "không đổi" trong quá trình đánh giá đa luồng. Tôi quan tâm đến cách dữ liệu đó hiển thị với các chủ đề có sẵn khác. Dường như bất kỳ khối đồng bộ nào, thậm chí w/o bất kỳ sự đồng bộ nào xảy ra trước khi quan hệ, gây ra khả năng hiển thị này. Hoặc có thể xảy ra trước khi không yêu cầu bất kỳ đồng bộ hóa rõ ràng nào và "không có công việc nào được chạy cho đến khi tất cả thay đổi giá trị được thực hiện" phù hợp với hóa đơn. –

+1

@AndrewRaffensperger Đúng. Nếu đó là tất cả những gì bạn cần, có một tiện ích 'java.util.concurrent' với chi phí tối thiểu cần thiết cho tính chính xác. Đó là một sai lầm khi cho rằng các tiện ích Concurrency có chi phí cao hơn; trên thực tế, họ cung cấp quyền truy cập vào các công cụ đồng thời hiệu suất cao như so sánh và trao đổi. Việc thực hiện điều này trong Java sẽ chậm hơn so với mã nguồn được tối ưu hóa phía sau các lớp 'AtomicXXX'. Có lợi thế hiệu suất tương tự trong hầu hết các tiện ích khác. – erickson

1

Tại sao bạn không thực hiện Collection<ValueRef>ValueRef không thay đổi hoặc ít nhất là không sửa đổi các giá trị trong bộ sưu tập sau khi bạn đã xuất bản tham chiếu đến bộ sưu tập. Sau đó, bạn sẽ không phải lo lắng về việc đồng bộ hóa.

Đó là khi bạn muốn thay đổi giá trị của bộ sưu tập, tạo bộ sưu tập mới và đặt các giá trị mới vào đó. Một khi các giá trị đã được thiết lập, vượt qua bộ sưu tập tham chiếu các đối tượng công việc mới.

Lý do duy nhất không làm điều này là nếu kích thước của bộ sưu tập quá lớn đến nỗi nó không vừa với bộ nhớ và bạn không thể có hai bản sao, hoặc trao đổi bộ sưu tập sẽ gây ra quá nhiều công việc cho garbage collector (chứng minh rằng một trong số này là một vấn đề trước khi bạn sử dụng một cấu trúc dữ liệu có thể thay đổi cho mã luồng).

+0

Phải, tôi luôn có thể xây dựng lại ValueRef hoặc xây dựng lại hồ bơi luồng và vấn đề của tôi biến mất. Nhưng trong thực tế thực tế của tôi, cấu trúc dữ liệu là rất phức tạp, và mã được gọi là thường xuyên đủ để xây dựng lại hồ bơi thread mỗi đánh giá sẽ là quá nhiều chi phí. –

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