A meno che tu non abbia una necessità molto particolare e insolita che non riesco a capire, mi sembra che il tuo obiettivo dovrebbe essere quello di proteggere il saldo del conto da eventuali corruzioni da parte di più thread che tentano di accreditare o addebitare un account allo stesso tempo.
Il modo per farlo sarebbe come questo:
public class Service {
public void transferMoney(Account fromAcct, Account toAcct, int amount) {
fromAcct.credit(amount);
toAccount.debit(amount);
}
}
class Account {
private final object syncObject = new Object();
private int amount = 0;
public void credit(int sum) {
synchronized(syncObject) {
amount = amount + sum;
}
}
public void debit(int sum) {
synchronized(syncObject) {
amount = amount - sum;
}
}
}
Se il vostro obiettivo durante il trasferimento di denaro è quello di garantire sempre che si verifichino azioni sia del credito e di debito come una transazione, o atomico, quindi utilizzando sincronizzazione non è l'approccio giusto Anche in un blocco sincronizzato, se dovesse verificarsi un'eccezione, si perde la garanzia che entrambe le azioni si verifichino atomicamente.
L'implementazione delle transazioni è un argomento molto complesso, motivo per cui utilizziamo normalmente i database per farlo per noi.
EDIT: OP chiesto: Qual è la differenza tra il mio esempio (un blocco mux sincronizzato) e il vostro sincronizzato in classe Account?
Questa è una domanda giusta. Ci sono alcune differenze. Ma direi che la differenza principale è che, ironia della sorte, il tuo esempio su -synchronizes. In altre parole, anche se ora utilizzi un singolo blocco sincronizzato, le tue prestazioni saranno in effetti molto peggiori, potenzialmente.
consideri il seguente esempio: Hai 4 conti bancari diversi: Diamo loro un nome, B, C, D. E ora avete 2 trasferimenti di denaro che vengono avviate al tempo stesso esatto:
- trasferire denaro dal conto a a B. conto
trasferimento
- di denaro dal conto C per tenere conto D.
penso che sarete d'accordo che, poiché i trasferimenti di denaro 2 si verificano a causa completamente separate s, che non ci dovrebbe essere alcun danno (nessun rischio di corruzione) nell'esecuzione di entrambi i trasferimenti di denaro allo stesso tempo, giusto?
Tuttavia, con il tuo esempio, i trasferimenti di denaro possono essere eseguiti solo uno dopo l'altro.Con il mio, entrambi i trasferimenti di denaro avvengono contemporaneamente, ma anche in sicurezza. Bloccherò solo se entrambi i trasferimenti di denaro tentano di "toccare" gli stessi account.
Ora immagina se usi questo codice per elaborare centinaia, migliaia o più trasferimenti di denaro simultanei. Quindi non c'è dubbio che il mio esempio eseguirà MOLTO meglio del tuo, pur mantenendo la sicurezza del thread e proteggendo la correttezza del saldo di un account.
In effetti, la mia versione del codice si comporta concettualmente molto più come il vostro codice di blocco sincronizzato 2 originale. Tranne i seguenti miglioramenti:
- Corregge il potenziale scenario di deadlock.
- L'intento è più chiaro.
- Fornisce un migliore incapsulamento. (Il che significa che anche se qualche altro codice al di fuori del metodo
transferMoney
provasse ad addebitare o accreditare alcuni importi, conserverei comunque la sicurezza del thread, mentre non lo faresti. So che hai detto che non è il tuo caso, ma con la mia versione, il design lo garantisce assolutamente)
A) No. B) Questo non è sicuro. http://stackoverflow.com/q/17268485/34397 – SLaks
Cosa succede se hai due istanze 'Service'? – immibis
Se si sta tentando di effettuare le transazioni 2 (credito + debito) transazionale, la sincronizzazione non è il modo per farlo. Se il tuo obiettivo è quello di assicurarti di non danneggiare il saldo di un account se più thread spostano denaro nello stesso account nello stesso momento, allora la sincronizzazione dovrebbe avvenire all'interno della classe Account. – sstan