Done correctly, yes. But you have to design the synchronization carefully. Improper or over use can lead to deadlocks, and it is easy to synchronize on the wrong thing (thus making it appear to be safe, but not protecting the data correctly), or under synchronize (for example, because you are using a List whose methods are all synchronized, you expect it to be safe to iterate over the list - which it is not.)
So you have to think and design your synchronization. Just putting synchronized blocks/methods is not enough.
Use appropriate synchronization in below sequence of preference
1. Reentrant read-write lock
2. Synchronized block
3. Synchronized method
Also, you can try actor based concurrency. This is a kind of pattern where you dedicate ONLY ONE thread to do mutations on a resource.
To avoid race conditions, the answer is religious code review, multi-threaded unit tests. There is no short cut.
The best thing would be to create side-effect free and stateless functions, use immutables as much as possible. But that is not possible always. So use java.util.concurrent.atomic, concurrent data structures like prefer ConcurrentHashMap instead of HashMap.
is the behavior of an electronic or software system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended.
In java first avoid access to shared field without one of the following synchronization actions, see java memory model for a detailed explanation: volatile field, synchronized statement, java.util.concurrent.locks or java.util.concurrent.atomic. The following class will give results dependent on how many cores your pc has:
Since each cpu core caches the data from main memory the result depends on how many threads run on the same core as the main thread. By using volatile, each read or write to the volatile field will flash the cache. In the example given above, this will not be enough since the block:
must be atomar, so that the result is independent of the scheduling of the threads. This can be done by putting an synchronized block around it.