2009-10-29 62 views
5

Tôi là tình huống sau.Làm thế nào để sử dụng ReadWriteLock?

Khi khởi động ứng dụng web, tôi cần tải Bản đồ sau đó được sử dụng bởi nhiều chủ đề đến. Đó là, các yêu cầu đến và Bản đồ được sử dụng để tìm hiểu xem nó có chứa một khóa cụ thể hay không và nếu như vậy giá trị (đối tượng) được lấy ra và liên kết với một đối tượng khác.

Hiện tại, nội dung của Bản đồ thay đổi. Tôi không muốn khởi động lại ứng dụng của mình để tải lại tình huống mới. Thay vào đó tôi muốn làm điều này một cách năng động.

Tuy nhiên, tại thời điểm Bản đồ đang tải lại (xóa tất cả các mục và thay thế chúng bằng bản đồ mới), các yêu cầu đọc đồng thời trên Bản đồ đó vẫn đến.

Tôi nên làm gì để ngăn tất cả các chủ đề đọc truy cập vào Bản đồ đó trong khi nó đang được tải lại? Làm thế nào tôi có thể làm điều này một cách hiệu quả nhất, bởi vì tôi chỉ cần điều này khi bản đồ được tải lại mà sẽ chỉ xảy ra rải rác (mỗi x tuần một lần)?

Nếu ở trên không phải là một tùy chọn (chặn) làm cách nào để đảm bảo rằng trong khi tải lại yêu cầu đọc của tôi sẽ không bị ngoại lệ không mong muốn (vì khóa không còn ở đó nữa hoặc giá trị không còn tồn tại hoặc bị tải lại)?

Tôi được đưa ra lời khuyên rằng một ReadWriteLock có thể giúp tôi. Bạn có thể cung cấp cho tôi một ví dụ về cách tôi nên sử dụng ReadWriteLock này với độc giả và nhà văn của tôi không?

Cảm ơn,
E

Trả lời

6

Tôi đề nghị để xử lý này như sau:

  1. Có bản đồ của bạn truy cập tại một vị trí trung tâm (có thể là một singleton mùa xuân, một tĩnh ...).
  2. Khi bắt đầu tải lại, hãy để ví dụ như hiện tại, hoạt động trong một phiên bản Bản đồ khác.
  3. Khi bản đồ mới được điền, hãy thay thế bản đồ cũ bằng bản đồ mới này (đó là một hoạt động nguyên tử).

Mẫu mã:

static volatile Map<U, V> map = ....; 

    // ************************** 

    Map<U, V> tempMap = new ...; 
    load(tempMap); 
    map = tempMap; 

tác Concurrency:

  • volatile giúp với tầm nhìn của biến để đề khác.
  • Trong khi tải lại bản đồ, tất cả các chủ đề khác nhìn thấy giá trị cũ không bị quấy rầy, vì vậy chúng không bị phạt gì cả.
  • Bất kỳ chuỗi nào truy lục bản đồ ngay lập tức trước khi nó được thay đổi sẽ hoạt động với các giá trị cũ.
    • Nó có thể yêu cầu một số bản đồ cũ giống nhau, rất phù hợp cho tính nhất quán của dữ liệu (không tải giá trị đầu tiên từ bản đồ cũ hơn và các giá trị khác từ bản đồ cũ hơn).
    • Nó sẽ kết thúc xử lý yêu cầu của nó với bản đồ cũ, nhưng yêu cầu tiếp theo sẽ hỏi lại bản đồ và sẽ nhận được các giá trị mới hơn.
+0

Điều này sẽ được chấp nhận trong điều kiện bạn không phải kiểm tra thứ gì đó trên bản đồ trước khi cập nhật là một hoạt động kiểm tra sau đó sửa đổi và do đó không phải là nguyên tử. – Adam

3

Nếu đề khách hàng không sửa đổi bản đồ, tức là nội dung của bản đồ là hoàn toàn phụ thuộc vào nguồn từ nơi nó được nạp, bạn chỉ có thể tải một bản đồ mới và thay thế các tham chiếu đến ánh xạ các chuỗi khách hàng của bạn đang sử dụng khi bản đồ mới được tải.

Khác sau đó sử dụng hai lần bộ nhớ trong một thời gian ngắn, không phát sinh hình phạt nào về hiệu suất.

Trong trường hợp bản đồ sử dụng quá nhiều bộ nhớ để có 2 bộ nhớ, bạn có thể sử dụng cùng một chiến thuật cho mỗi đối tượng trên bản đồ; lặp lại trên bản đồ, xây dựng một đối tượng mới được ánh xạ tới và thay thế ánh xạ ban đầu khi đối tượng được nạp.

+0

Làm điều này trên cơ sở mỗi đối tượng sẽ để lại bản đồ ở trạng thái hỗn hợp, mặc dù --- một số đối tượng sẽ từ bản đồ cũ, một số từ bản đồ mới. Cho dù đó là vấn đề phụ thuộc vào trường hợp sử dụng. – uckelman

2

Lưu ý rằng việc thay đổi tài liệu tham khảo theo đề nghị của người khác có thể gây ra vấn đề nếu bạn dựa vào bản đồ là không thay đổi trong một thời gian (ví dụ if (map.contains(key)) {V value = map.get(key); ...} Nếu bạn cần điều đó, bạn nên giữ một tham chiếu địa phương để các bản đồ:.

static Map<U,V> map = ...; 

void do() { 
    Map<U,V> local = map; 
    if (local.contains(key)) { 
    V value = local.get(key); 
    ... 
    } 
} 

EDIT:

Giả định là bạn không muốn đồng bộ hóa tốn kém cho các đề tài khách hàng của mình. bỏ qua bất kỳ thay đổi nào đối với bản đồ đã xảy ra trong khi nó đang chạy. Bằng cách này, bạn có thể an toàn thực hiện một số ass các giả định về bản đồ của bạn - ví dụ: một khóa có mặt và luôn được ánh xạ tới cùng một giá trị trong khoảng thời gian của một yêu cầu duy nhất. Trong ví dụ trên, nếu chủ đề đọc của bạn thay đổi bản đồ ngay sau khi một khách hàng gọi là map.contains(key), khách hàng có thể nhận được null trên map.get(key) - và bạn gần như chắc chắn sẽ kết thúc yêu cầu này với một NullPointerException. Vì vậy, nếu bạn đang thực hiện nhiều lần đọc bản đồ và cần phải thực hiện một số giả định như đã đề cập trước đây, thì dễ nhất là giữ một tham chiếu cục bộ tới bản đồ (có thể đã lỗi thời).

Từ khóa dễ bay hơi không thực sự cần thiết ở đây. Nó sẽ chỉ đảm bảo rằng bản đồ mới được sử dụng bởi các chủ đề khác ngay khi bạn thay đổi tham chiếu (map = newMap). Nếu không có biến động, một lần đọc tiếp theo (local = map) vẫn có thể trả về tham chiếu cũ một thời gian (chúng ta đang nói ít hơn một nano giây) - đặc biệt là trên các hệ đa lõi nếu tôi nhớ chính xác. Tôi sẽ không quan tâm đến nó, nhưng f bạn cảm thấy cần thiết cho rằng thêm chút vẻ đẹp đa luồng, miễn phí của bạn để sử dụng nó tất nhiên;)

+0

Bạn có nghĩa là tĩnh Bản đồ dễ bay hơi map = ..; hoặc không tạo sự khác biệt? Vì vậy, khi mapreloader của tôi (thread khác) map = "map mới tạo" trong khi thread đọc của tôi truy cập một tham chiếu cục bộ của bản đồ đó, người đọc của tôi không biết điều đó và tiếp tục truy cập bản đồ "cũ" cho đến khi nó thực hiện cục bộ = bản đồ ? Đúng không ? – EdwinDhondt

+0

xem chỉnh sửa của tôi ở trên – sfussenegger

+0

Nhưng trong cả hai phương pháp cục bộ = bản đồ (có hoặc không có biến động), tôi sẽ không bao giờ nhận được các ngoại lệ nullpointer, chỉ có thể là một số dữ liệu cũ? Có đúng không ? – EdwinDhondt

2

Tôi thích giải pháp Bản đồ dễ bay hơi từ KLE rất nhiều và sẽ đi với. Một ý tưởng khác mà ai đó có thể thấy thú vị là sử dụng bản đồ tương đương với một CopyOnWriteArrayList, về cơ bản là một CopyOnWriteMap. Chúng tôi xây dựng một trong những nội và nó là không tầm thường, nhưng bạn có thể có thể tìm thấy một COWMap ra trong tự nhiên:

http://old.nabble.com/CopyOnWriteMap-implementation-td13018855.html

1

Đây là câu trả lời từ các javadocs JDK cho ReentrantReadWriteLock implementation of ReadWriteLock. Một vài năm trễ nhưng vẫn hợp lệ, đặc biệt nếu bạn không muốn chỉ dựa vào volatile

class RWDictionary { 
    private final Map<String, Data> m = new TreeMap<String, Data>(); 
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 
    private final Lock r = rwl.readLock(); 
    private final Lock w = rwl.writeLock(); 

    public Data get(String key) { 
     r.lock(); 
     try { return m.get(key); } 
     finally { r.unlock(); } 
    } 
    public String[] allKeys() { 
     r.lock(); 
     try { return m.keySet().toArray(); } 
     finally { r.unlock(); } 
    } 
    public Data put(String key, Data value) { 
     w.lock(); 
     try { return m.put(key, value); } 
     finally { w.unlock(); } 
    } 
    public void clear() { 
     w.lock(); 
     try { m.clear(); } 
     finally { w.unlock(); } 
    } 
} 
Các vấn đề liên quan