javamultithreadingsynchronizedjava-threadssynchronized-block

Does synchronized (this) lock only the synchronized block or all the "this" code?


public class ObjectCounter {
    private static long numOfInstances = 0;
    public ObjectCounter(){
        synchronized(this){
        numOfInstances++;
        }
    }
    **public static synchronized long getCount(){
        return numOfInstances;
    }**
//vs//
    **public static long getCount(){
        return numOfInstances;
    }**
}

if I'll run few threads, some of them call the static function getCount() and some of them create new instances. I want to get in each call to getCount() the real number of instances at the time.

  1. Is there a difference between the two options in the code?
  2. If I lock "this" shouldn't it mean that I can't call getCount() until the constructor exits the synchronized block (lets say if I don't write synchronize on the getCount()).
  3. if I do a synchronized block in some place in the code, does it lock only the synchronized block or all the "this" code?
  4. From here down EDIT: thank you all, it was very helpful, but I have a few more questions following your answers.
  5. If I understand correctly, the synchronized(this) block doesn't effect (or connected to) the static synchronized function (in lock terms not the numOfInstances increment)?
  6. is there a better option to make the increment and the getCount() function Thread-safe? (like open a static object and do synchronized(obj) instead synchronized(this) - friend suggested).
  7. If I had a f1() method (non-static) in ObjectCounter class, while one thread is in the synchronized(this) can other thread enter f1() block (not a synchronized class or have synchronized block inside)?
  8. If I had a f1() method (non-static) and f2() method (non-static) in ObjectCounter, in f1() I have synchronized(this) block. while one thread is in the synchronized(this) block, can other thread enter f1() block (not a synchronized class or have synchronized block inside)? (lets say both of the threads "working" on the same instance)

`


Solution

  • Using synchronized means in order for a thread to execute that block or method, it has to acquire a lock referenced (explicitly or implicitly) by that block or method. For the static synchronized methods, that lock is the monitor on the class object. For the synchronized(this) block, the lock used is the monitor on the current instance. Sharing of locks between multiple methods or blocks is what enforces atomicity and memory visibility of updates, also the shared lock provides a shared communication path through which waiting and notification can take place.

    Since the static synchronized blocks use a different lock from that used by the block in the constructor, entering a static synchronized block is not blocked by another thread's accessing the block that requires acquiring the lock on the current instance, and the synchronized block in the constructor has no effect on anything, the lock acquisition will always be uncontended. More importantly here, changes made by one thread in the constructor may not get seen by other threads using the getter. Synchronization affects both locking and memory visibility.

    This changed version would work:

    public class ObjectCounter {
        private static long numOfInstances = 0;
        public ObjectCounter(){
            synchronized(ObjectCounter.class){
                numOfInstances++;
            }
        }
        public static synchronized long getCount(){
            return numOfInstances;
        }
    }
    

    because the getter and the incrementing block are using the same lock. Making the different threads acquire the same monitor ensures that the change to the counter gets safely published so that another thread accessing the getter can see the updated value.

    The synchronized keyword says, "you have to acquire a lock before you can enter", where for the method the lock is assumed: with the static keyword on the method it's the monitor on the class, without a static keyword it's the monitor on the current instance. For locking to work correctly the different blocks and methods need to use the same lock. There is arguably too much syntax sugar and too much making things convenient in how Java was designed: allowing implicit choice of locks and putting the monitor on java.lang.Object can cause confusion.

    WRT your question #6: For what you're doing here you'd be better off with an AtomicLong. Use synchronized blocks for coordinating multiple changes that need to take place without interference from other threads.

    Questions #3, #7 and #8 seem very similar: If a method/block isn't attempting to acquire a lock, nothing prevents threads from executing that method/block. The object as a whole doesn't get any protection, using the synchronized methods or blocks to enforce locking is what does the protecting. Think less in terms of "using the synchronized keyword" and more in terms of what lock threads need to acquire.