2008-10-16 37 views
7

Tôi có một đối tượng Danh sách được truy cập bởi nhiều luồng. Có chủ yếu là một chủ đề, và trong một số điều kiện hai chủ đề, cập nhật danh sách. Có một đến năm luồng có thể đọc từ danh sách này, tùy thuộc vào số lượng yêu cầu của người dùng đang được xử lý. Danh sách không phải là một chuỗi nhiệm vụ cần thực hiện, nó là danh sách các đối tượng miền đang được truy lục và cập nhật đồng thời.Cách tiếp cận tốt nhất để sử dụng trong Java 6 cho Danh sách được truy cập đồng thời

Bây giờ có một số cách để làm cho truy cập vào danh sách này thread-safe:
-sử dụng đồng bộ khối
-sử dụng bình thường Khóa (tức là đọc và viết ops chia sẻ cùng khóa)
-sử dụng ReadWriteLock
-sử dụng là một trong những ConcurrentBLABLBA lớp bộ sưu tập mới

câu hỏi của tôi:
Cách tiếp cận tối ưu để sử dụng là gì, vì phần cricital thường không chứa nhiều thao tác (chủ yếu chỉ cần thêm/xóa/chèn hoặc lấy các phần tử trong danh sách)?
Bạn có thể đề xuất một cách tiếp cận khác, không được liệt kê ở trên không?

Một số ép
hiệu suất -optimal là rất quan trọng, sử dụng bộ nhớ không quá nhiều
-nó phải là một danh sách có thứ tự (hiện đồng bộ hóa trên một ArrayList), mặc dù không phải là một danh sách được sắp xếp (tức là không được sắp xếp sử dụng So sánh hoặc so sánh, nhưng theo thứ tự chèn)
danh sách sẽ lớn, chứa tối đa 100.000 đối tượng miền, do đó sử dụng một cái gì đó như CopyOnWriteArrayList không khả thi
phần viết/cập nhật phần mềm thường rất nhanh, làm thêm đơn giản/xóa/chèn hoặc thay thế (bộ)
các hoạt động đọc sẽ chủ yếu gọi hàm elementAt (index) hầu hết thời gian, mặc dù một số hoạt động đọc có thể thực hiện tìm kiếm nhị phân hoặc indexOf (phần tử)
-Không lặp lại trực tiếp trong danh sách được thực hiện, indexOf (..) sẽ duyệt qua danh sách

Trả lời

3

Bạn có phải sử dụng danh sách tuần tự không? Nếu cấu trúc kiểu bản đồ phù hợp hơn, bạn có thể sử dụng ConcurrentHashMap. Với một danh sách, ReadWriteLock có lẽ là cách hiệu quả nhất.

Chỉnh sửa để phản ánh chỉnh sửa của OP: Tìm kiếm nhị phân trên thứ tự chèn? Bạn có lưu trữ dấu thời gian và sử dụng dấu thời gian đó để so sánh, trong tìm kiếm nhị phân không? Nếu có, bạn có thể sử dụng dấu thời gian làm khóa và ConcurrentSkipListMap làm vùng chứa (duy trì thứ tự chính).

+0

Tôi thích ý tưởng ConcurrentSkipListMap. Trong 90% thời gian danh sách được sắp xếp theo một số dấu thời gian (một phần của ID của mỗi đối tượng tên miền), do đó, nó có thể là giá trị nó tối ưu hóa cho điều đó. Vẫn sẽ nghĩ về 10% còn lại. –

1

Chủ đề đọc đang làm gì? Nếu họ đang lặp qua danh sách, thì bạn thực sự cần phải chắc chắn rằng không ai chạm vào danh sách trong toàn bộ quá trình lặp lại, nếu không bạn có thể nhận được kết quả rất kỳ quặc.

Nếu bạn có thể xác định chính xác ngữ nghĩa bạn cần, bạn có thể giải quyết vấn đề - nhưng bạn cũng có thể thấy rằng bạn cần phải viết loại bộ sưu tập của riêng mình để làm đúng và hiệu quả. Ngoài ra, CopyOnWriteArrayList cũng có thể đủ tốt - nếu có thể tốn kém. Về cơ bản, càng có nhiều bạn có thể tie xuống yêu cầu của bạn, hiệu quả hơn nó có thể được.

+0

Tôi đã xem xét CopyOnWriteArrayList, nhưng điều đó sẽ quá tốn kém để sử dụng. Danh sách này có khả năng có 100000 phần tử và được cập nhật rất nhiều. –

+2

Được rồi, trong trường hợp đó bạn sẽ cần phải thực sự làm việc ra ngữ nghĩa của bạn một cách cẩn thận. Các bản cập nhật này có thực hiện chèn/xóa hay chỉ thay thế các phần tử không? Nếu họ đang thêm/xóa, họ đang làm như vậy ở đầu/đuôi của danh sách? (Trong trường hợp đó một danh sách liên kết thực sự có thể giúp bạn.) –

1

Tôi không biết nếu điều này là một giải pháp cho vấn đề posible nhưng ... nó làm cho tinh thần để tôi sử dụng một người quản lý cơ sở dữ liệu để cho rằng số lượng lớn dữ liệu và để cho nó quản lý các giao dịch

+0

Dữ liệu được quản lý ở phía máy chủ bởi trình quản lý DB và lớp Máy chủ ứng dụng, nhưng chúng tôi cần bằng cách nào đó hiển thị nó cho người dùng cuối.Trình quản lý danh sách này xảy ra ở phía máy khách nơi dữ liệu được truy lục. –

1

tôi thứ hai Telcontar's suggestion của một cơ sở dữ liệu, vì chúng thực sự được thiết kế để quản lý quy mô dữ liệu này và thương lượng giữa các luồng, trong khi bộ nhớ trong bộ nhớ thì không.

Bạn nói rằng dữ liệu nằm trên cơ sở dữ liệu trên máy chủ và danh sách cục bộ trên máy khách là vì giao diện người dùng. Bạn không cần phải giữ tất cả 100000 mục trên máy khách cùng một lúc hoặc thực hiện các chỉnh sửa phức tạp trên đó. Dường như với tôi rằng những gì bạn muốn trên máy khách là một bộ nhớ cache nhẹ vào cơ sở dữ liệu.

Viết bộ nhớ cache chỉ lưu trữ tập hợp con dữ liệu hiện tại trên máy khách cùng một lúc. Bộ nhớ cache của khách hàng này không thực hiện các chỉnh sửa đa luồng phức tạp trên dữ liệu riêng của nó; thay vào đó, nó cung cấp tất cả các chỉnh sửa thông qua máy chủ và lắng nghe các bản cập nhật. Khi dữ liệu thay đổi trên máy chủ, máy khách chỉ đơn giản là quên và dữ liệu cũ và tải lại. Chỉ một chuỗi được chỉ định mới được phép đọc hoặc ghi chính bộ sưu tập đó. Bằng cách này, khách hàng chỉ đơn giản là phản ánh các chỉnh sửa xảy ra trên máy chủ, thay vì cần các chỉnh sửa phức tạp.

Vâng, đây là một giải pháp khá phức tạp. Các thành phần của nó là:

  • Một giao thức cho tải một loạt các dữ liệu, nói mục 478.712-478.901, chứ không phải là toàn bộ điều
  • Một giao thức để tiếp nhận thông tin cập nhật về dữ liệu thay đổi
  • Một lớp bộ nhớ cache lưu trữ các mục theo chỉ mục đã biết của chúng trên máy chủ
  • Một chuỗi thuộc bộ nhớ cache được kết nối với máy chủ đó. Đây là chủ đề duy nhất mà ghi vào bộ sưu tập riêng của mình
  • Một chủ đề thuộc bộ nhớ cache mà xử lý callbacks khi dữ liệu được lấy
  • Một giao diện UI thành phần thực hiện để cho phép họ nhận được dữ liệu khi nó đã được tải

tại đâm đầu tiên, xương của bộ nhớ cache này có thể trông như thế này:

class ServerCacheViewThingy { 
    private static final int ACCEPTABLE_SIZE = 500; 
    private int viewStart, viewLength; 
    final Map<Integer, Record> items 
      = new HashMap<Integer, Record>(1000); 
    final ConcurrentLinkedQueue<Callback> callbackQueue 
      = new ConcurrentLinkedQueue<Callback>(); 

    public void getRecords (int start, int length, ViewReciever reciever) { 
     // remember the current view, to prevent records within 
     // this view from being accidentally pruned. 
     viewStart = start; 
     viewLenght = length; 

     // if the selected area is not already loaded, send a request 
     // to load that area 
     if (!rangeLoaded(start, length)) 
      addLoadRequest(start, length); 

     // add the reciever to the queue, so it will be processed 
     // when the data has arrived 
     if (reciever != null) 
      callbackQueue.add(new Callback(start, length, reciever)); 
    } 

    class Callback { 
     int start; 
     int length; 
     ViewReciever reciever; 
     ... 
    } 

    class EditorThread extends Thread { 

     private void prune() { 
      if (items.size() <= ACCEPTABLE_SIZE) 
       return; 
      for (Map.Entry<Integer, Record> entry : items.entrySet()) { 
       int position = entry.key(); 
       // if the position is outside the current view, 
       // remove that item from the cache 
       ... 
      } 
     } 

     private void markDirty (int from) { ... } 

     .... 
    } 

    class CallbackThread extends Thread { 
     public void notifyCallback (Callback callback); 
     private void processCallback (Callback) { 
      readRecords 
     } 
    } 
} 

interface ViewReciever { 
    void recieveData (int viewStart, Record[] records); 
    void recieveTimeout(); 
} 

có rất nhiều chi tiết mà bạn sẽ phải điền vào cho chính mình, rõ ràng.

+0

Cảm ơn bạn đã trả lời. Lên đến 100000 mục là "trang" dữ liệu chúng tôi đang truy xuất cho khách hàng. DB có thể chứa hàng tỷ mục nhập. Những gì tôi sẽ xem xét nghiêm túc là tư vấn của bạn về việc truy cập vào danh sách chỉ với một chủ đề. Điều này chắc chắn sẽ đơn giản hóa mọi thứ. –

1

Bạn có thể sử dụng một wrapper mà thực hiện đồng bộ hóa:

import java.util.Collections; 
import java.util.ArrayList; 

ArrayList list = new ArrayList(); 
List syncList = Collections.synchronizedList(list); 

// make sure you only use syncList for your future calls... 

Đây là một giải pháp dễ dàng. Tôi sẽ thử điều này trước khi sử dụng các giải pháp phức tạp hơn.

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