2013-05-30 39 views
5

Tôi muốn đồng bộ hóa các cuộc gọi phương thức trên cơ sở một số id giống như một Trang trí đồng thời của một cá thể đối tượng nhất định.
Ví dụ:
Tất cả các chuỗi gọi phương thức có tham số "id1", nên thực thi serially với nhau.
Tất cả các phần còn lại, gọi phương thức với đối số khác nhau, nói "id2", nên thực hiện song song với các chuỗi gọi phương thức có tham số "id1", nhưng lại liên tục với nhau.Đồng bộ hóa/khóa các cuộc gọi phương thức hạt mịn dựa trên các tham số phương pháp

Vì vậy, trong tâm trí của tôi điều này có thể được thực hiện bằng cách có một khóa (http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html) trường hợp cho mỗi tham số phương pháp như vậy. Mỗi lần phương thức được gọi với thông số, trường hợp khóa tương ứng với giá trị param cụ thể (ví dụ: "id1") sẽ được tìm kiếm và chuỗi hiện tại sẽ cố gắng lấy khóa.

Phát biểu trong mã:

public class ConcurrentPolicyWrapperImpl implements Foo { 

private Foo delegate; 

/** 
* Holds the monitor objects used for synchronization. 
*/ 
private Map<String, Lock> concurrentPolicyMap = Collections.synchronizedMap(new HashMap<String, Lock>()); 

/** 
* Here we decorate the call to the wrapped instance with a synchronization policy. 
*/ 
@Override 
public Object callFooDelegateMethod (String id) { 
     Lock lock = getLock(id); 
     lock.lock(); 
     try { 
      return delegate.delegateMethod(id); 
     } finally { 
      lock.unlock(); 
     } 
} 


protected Lock getLock(String id) { 
     Lock lock = concurrentPolicyMap.get(id); 

     if (lock == null) { 
      lock = createLock(); 
      concurrentPolicyMap.put(id, lock); 

     } 
     return lock; 
    } 

} 

protected Lock createLock() { 
     return new ReentrantLock(); 
    } 

Dường như việc này - Tôi đã làm một số thử nghiệm hiệu suất với jmeter và vân vân. Tuy nhiên, như chúng ta đều biết rằng sự tương tranh trong Java là một điều phức tạp, tôi quyết định hỏi ý kiến ​​của bạn ở đây.

Tôi không thể ngừng nghĩ rằng có thể có cách tốt hơn để thực hiện việc này. Ví dụ bằng cách sử dụng một trong các triển khai BlockingQueue. Bạn nghĩ sao?

Tôi cũng không thể thực sự quyết định xem có vấn đề đồng bộ hóa tiềm năng nào với việc khóa hay không, tức là phương pháp protected Lock getLock(String id). Tôi đang sử dụng một bộ sưu tập đồng bộ, nhưng là đủ? I E. không phải là nội dung như sau thay vì nội dung tôi hiện có:

protected Lock getLock(String id) { 
    synchronized(concurrentPolicyMap) { 
    Lock lock = concurrentPolicyMap.get(id); 

    if (lock == null) { 
     lock = createLock(); 
     concurrentPolicyMap.put(id, lock); 

    } 
    return lock; 
    } 

} 

Vậy các bạn nghĩ sao?

Trả lời

3

Khóa các vấn đề về tạo sang một bên, mẫu được OK ngoại trừ việc bạn có thể có số lượng khóa không bị chặn. Nói chung mọi người tránh điều này bằng cách tạo/sử dụng khóa sọc. Có một triển khai tốt/đơn giản trong thư viện ổi.

Application area of lock-striping

How to acquire a lock by a key

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/Striped.html

Ví dụ mã sử dụng ổi thực hiện:

private Striped<Lock> STRIPPED_LOCK = Striped.lock(64); 

public static void doActualWork(int id) throws InterruptedException { 
    try { 
     STRIPPED_LOCK.get(id).lock(); 
     ... 
    } finally { 
     STRIPPED_LOCK.get(id).unlock(); 
    } 
} 
+0

Bạn đang nói "Khóa vấn đề sáng tạo sang một bên" ... Bạn có phát hiện ra bất kỳ hoặc ý của bạn là bạn không bình luận về nó? – Svilen

+0

Nhìn vào vấn đề đó chặt chẽ hơn bây giờ-- vâng, bạn sẽ definitley cần khối đồng bộ trong phương thức getLock() của bạn. Như bạn có trong ví dụ thứ hai. Nếu không, hình ảnh (a) 5 chủ đề gọi getLock() cùng một lúc, cho cùng một ID; (b) tất cả đều tìm thấy khóa null; (c) tất cả chúng tạo ra và trả về một thể hiện khóa mới. Thất bại. Khối đồng bộ hóa trong mã bit thứ hai của bạn tránh được điều đó. – Keith

+0

Cảm ơn, Keith. Btw, bạn có lẽ có bất kỳ ý tưởng làm thế nào để làm điều tương tự nhưng với các nhà điều hành/nhiệm vụ. I E. có một ThreadPoolExecutor duy nhất nhưng thực hiện nhiệm vụ phân vùng bằng một khóa. Ví dụ các nhiệm vụ cho khóa "id1" sẽ chạy theo kiểu nối tiếp với nhau, đồng thời song song với tất cả các nhiệm vụ khác cho các khóa "id2", "id3", vv ... – Svilen

1

Mặc dù cá nhân tôi muốn tiếp cận Striped<Lock> ổi của đề xuất bởi Keith, chỉ để thảo luận & đầy đủ, ID muốn chỉ ra rằng sử dụng Proxy động hoặc AOP chung hơn (Lập trình hướng khía cạnh), là một cách tiếp cận. Vì vậy, chúng tôi sẽ xác định một giao diện IStripedConcurrencyAware có thể dùng làm "một thứ như một trang trí tương tranh" mà bạn mong muốn, và phương pháp chiếm đoạt phương pháp Dynamic Proxy/AOP dựa trên giao diện này sẽ tách ghép cuộc gọi phương thức thành Executor thích hợp/Thread.

Cá nhân tôi không thích AOP (hoặc hầu hết mùa xuân, cho vấn đề đó) bởi vì nó phá vỡ những gì bạn thấy, bạn sẽ thấy đơn giản về lõi Java, nhưng YMMV.

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