People tell about two types of multi-threaded locking - object and class.
A Class is an Object. There is only one kind of locking in the Java language: Every Object (including every Class) has a mutex that can be locked by a synchronized
block or a synchronized
method. The Object to be locked is implicit in a synchronized
method: It's the "this" instance for an instance method, and it's the Class object for a static method.
One of the most common newbie mistakes is to think that two different threads can't get into the same synchronized
block at the same time. They can, and there's plenty of questions and answers here in StackOverflow that prove it. Another mistake is to think that if one thread is synchronized on some object, then other threads will not be able to modify the object. They can and they do.
Synchronization prevents two or more threads from synchronizing on the same Object at the same time. Which object is the right object? It's all about protecting your data. If the structure that you want to protect is a linked list, for example, then a good choice would be for any method that accesses the list to synchronize on the list header. If you want to protect global data (e.g., static
variables), then you want to synchronize on a global object (e.g., the Class object that owns the variables.) The important thing is that, if you have read/write data (a.k.a., "mutable data") that are accessed by more than one thread, then every method that accesses the same data must synchronize on the same lock.
There is another kind of locking in Java, but it's not in the Java language; it's in the Java standard library. It's available through objects that implement the java.util.concurrent.locks.Lock interface. Of course a Lock object (like any Object) also implements the first kind of locking, but you should never, ever, synchronize on a Lock object unless you want to give people the impression that you are a clueless newbie.
The java.util.concurrent-style locking is more powerful than using synchronized
blocks because of it's explicit lock() and unlock() methods. For example, it is possible for one method to lock a lock, and a different method to unlock it. That can lead to code that is tricky to understand, and I wouldn't do it unless I had a very good reason, but sometimes there are good reasons.