Esistono molte tecniche che non richiedono la sincronizzazione esplicita. 
 Ad esempio, se si mantengono tutti i dati locali in ciascun thread e si comunica solo tramite messaggi di passaggio, la sincronizzazione viene eseguita implicitamente inviando e in attesa di messaggi. 
I 
  il passaggio dei messaggi  hanno il vantaggio di essere scalabili molto bene, poiché non richiedono un  architettura di memoria unificata  e ci sono tecniche formali per il rilevamento e la prevenzione del deadlock, ad esempio  La comunicazione di processi sequenziali (CSP)  ti aiuta a ragionare formalmente su tali sistemi. 
 Un'altra opzione è l'uso di set di dati immutabili. Se nulla può cambiare, non hai mai bisogno di sincronizzarti, perché non ci sarà mai la possibilità che più di un thread proverà a scrivere contemporaneamente. 
 Puoi persino combinarli, se un processo crea insiemi di dati di memoria condivisa immutabili, può utilizzare i messaggi per passare i puntatori ai consumatori di tali dati. 
 Infine, potresti pensare in termini di livelli più alti di astrazioni. Unisci i tuoi processi insieme  BlockingQueue  da java.util.concurrent  e non è necessario pensare al l'effettiva implementazione della coda, tu li produci solo con un processo e li consumi in un altro. 
 C'è anche un livello più basso di astrazione rispetto a   synchronized   , usando le primitive come  Semaphor . 
 Nel complesso, è un'area ricca e profonda da studiare, e comprendere i pro e i contro di ciascuna tecnica ha il potenziale per renderti un programmatore migliore.