Ogni volta che è possibile accedere a una raccolta da più thread senza il blocco a un livello superiore (ad esempio su una base per transazione, piuttosto che per operazione), è possibile che qualcosa vada storto. Non è possibile eseguire operazioni condizionalmente su alcuna proprietà della raccolta, poiché tale proprietà potrebbe cambiare tra test ed esecuzione. Non è possibile eseguire un'iterazione sulla raccolta, poiché potrebbe cambiare durante il processo di iterazione. Tutto ciò che puoi fare è aggiungere elementi ad esso. Pertanto, ogni volta che ti ritrovi a voler utilizzare una raccolta sincronizzata, fai un passo indietro: quello che stai facendo è probabilmente il modo sbagliato di risolvere il tuo problema.
La sincronizzazione deve essere a un livello superiore rispetto alla raccolta oppure è necessario utilizzare una raccolta che implementa la logica di livello superiore richiesta dall'applicazione, ad es. una coda.
Per il tuo esempio specifico, il potenziale problema è che un oggetto uguale all'elemento che si sta aggiungendo potrebbe essere aggiunto da un altro thread, causando l'operazione di aggiunta nel codice che si cita per fallire. L'operazione remove rimuoverà quindi l'elemento aggiunto dall'altro thread, il che probabilmente non è quello che desideri (il codice sembra avere l'intento di lasciare la raccolta invariata al termine, ma cambiando temporaneamente durante un'operazione).
Ogni volta che è possibile, è meglio evitare di condividere l'accesso a basso livello ai valori mutabili tra i thread, e problemi come questo sono la ragione principale per cui.