There are many techniques which don't require explicit synchronisation.
For example, if you keep all data local to each thread and communicate only via passing messages, then the synchronisation is performed implicitly by sending and awaiting messages.
Message passing systems have the advantage that they scale very well, since they don't require a unified memory architecture and there are formal techniques for detecting and preventing deadlock, for instance Communicating Sequential Processes (CSP) helps you formally reason about such systems.
Another option is the use of immutable data sets. If nothing can change, you never need to synchronise, because there will never be any chance that more than one thread will try to write at once.
You can even combine them, if one process creates immutable shared memory data sets, it can use messages to pass pointers to consumers of that data.
Finally, you could think in terms of higher levels of abstractions. Tie your processes together by BlockingQueue from java.util.concurrent and you don't need to think about the actual implementation of the Queue, you just produce them in with one process and consume them in another.
There is even a lower level of abstraction than
synchronized, using primitives such as Semaphor.
In all, it is a rich and deep area to study, and understanding the pros and cons of each technique has the potential to make you a better programmer.