Desidero sincronizzare le chiamate di metodo sulla base di un ID, ad esempio un decoratore di concorrenza di una determinata istanza di oggetto.
Ad esempio:
Tutti i thread che chiamano il metodo con param "id1", devono essere eseguiti in serie l'un l'altro.
Tutti gli altri, che chiamano il metodo con argomenti diversi, ad esempio "id2", devono essere eseguiti in parallelo ai thread che chiamano il metodo con param "id1", ma di nuovo in serie tra loro.Sincronizzazione fine/blocco delle chiamate di metodo basate sui parametri del metodo
Quindi nella mia mente questo può essere implementato avendo un'istanza di blocco (http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html) per tale parametro param. Ogni volta che il metodo viene chiamato con il parametro, l'istanza di blocco corrispondente al valore del parametro specifico (ad esempio "id1") verrà cercata e il thread corrente cercherà di ottenere il blocco.
Parlando in codice:
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();
}
Sembra che questo funziona - ho fatto qualche test delle prestazioni con JMeter e così via. Ancora, come tutti sappiamo che la concorrenza in Java è una cosa complicata, ho deciso di chiedere la tua opinione qui.
Non riesco a smettere di pensare che potrebbe esserci un modo migliore per farlo. Ad esempio, utilizzando una delle implementazioni di BlockingQueue. Cosa ne pensi?
Inoltre non posso davvero decidere con certezza se c'è un potenziale problema di sincronizzazione con il blocco, ad esempio il metodo protected Lock getLock(String id)
. Sto usando una collezione sincronizzata, ma è abbastanza? Cioè non dovrebbe essere qualcosa di simile al seguente invece di quello che attualmente ho:
protected Lock getLock(String id) {
synchronized(concurrentPolicyMap) {
Lock lock = concurrentPolicyMap.get(id);
if (lock == null) {
lock = createLock();
concurrentPolicyMap.put(id, lock);
}
return lock;
}
}
Allora, cosa ne pensate voi ragazzi?
Stai dicendo "Blocca i problemi di creazione da parte" ... Hai notato qualcosa o intendi che non stai commentando su di esso? – Svilen
Guardando il problema più da vicino ora - sì, avresti sicuramente bisogno del blocco sincronizzato nel tuo metodo getLock(). Come hai nel secondo esempio. Altrimenti, l'immagine (a) 5 thread chiama getLock() allo stesso tempo, per lo stesso ID; (b) trovano tutti il blocco nullo; (c) tutti creano e restituiscono una nuova istanza di blocco. Fallire. Il blocco di sincronizzazione nel secondo bit di codice lo evita. – Keith
Grazie, Keith. A proposito, hai qualche idea su come fare la cosa simile ma con esecutori/compiti. Cioè avere un singolo ThreadPoolExecutor ma eseguire l'esecuzione di una partizione con una chiave. Ad esempio, le attività per la chiave "id1" verranno eseguite in modo seriale tra loro, mentre allo stesso tempo in parallelo a tutte le altre attività per le chiavi "id2", "id3", ecc ... – Svilen