Java - Intrinsic Locks and Synchronization

[Updated: Feb 7, 2017, Created: Mar 27, 2016]

An intrinsic lock (aka monitor lock) is an implicit internal entity associated with each instance of objects.

The intrinsic lock enforces exclusive access to an object's state. Here 'access to an object' refers to an instance method call.

When a synchronized method is called from a thread it needs to acquire the intrinsic lock. The lock is release when the thread is done executing the method or an exception is thrown in the method which is not handled/caught.

As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other threads will block when they attempt to acquire the lock. The blocked threads will wait till the lock is released by the currently executing thread.



Thread.sleep() inside the synchronized method doesn't release the lock.

Intrinsic lock are reentrant. That means once a thread has acquired the lock on a method it doesn't need to acquired the lock on calling other method of the same object. The other method doesn't need to necessarily have 'synchronized' keyword.

Constructors cannot be synchronized. Using the synchronized keyword with a constructor is a syntax error. Synchronizing constructors doesn't make sense, because only the thread that creates an object should have access to it while it is being constructed.

A static method can have synchronized keyword. In this case, the thread acquires the intrinsic lock for the Class object associated with the class rather than an instance of the class.


Let's understand 'intrinsic lock' concepts with examples:

How threads are blocked on 'intrinsic lock'?

The 'synchronized' method calls in a given thread will wait/block if there's already another thread is executing the same method or some other 'synchronized' method of the same instance. From a programmer perspective, the block is similar to other blocking calls e.g. Thread.join(). The method call blocks to get the instrict lock at the point of the synchronized method call as shown in the following example.

public class ThreadLockDemo {

    public static void main (String[] args) throws InterruptedException {
        ThreadLockDemo demo = new ThreadLockDemo();
        Thread thread1 = new Thread(() -> {
            System.out.println("thread1 before call "+ LocalDateTime.now());
            demo.syncMethod("from thread1");
            System.out.println("thread1 after call "+LocalDateTime.now());
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2 before call "+LocalDateTime.now());
            demo.syncMethod("from thread2");
            System.out.println("thread2 after call "+LocalDateTime.now());
        });

        thread1.start();
        thread2.start();
    }

    private synchronized void syncMethod (String msg) {
        System.out.println("in the sync method "+msg+" "+LocalDateTime.now());
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

We are using Thread#sleep method inside the synchronized method to keep the lock for a long time. sleep method doesn't release the lock but Object#wait does which we will explore in upcoming tutorials.

Output:

thread1 before call 2016-03-27T18:08:30.872
thread2 before call 2016-03-27T18:08:30.872
in the sync method from thread1 2016-03-27T18:08:30.886
thread1 after call 2016-03-27T18:08:35.887
in the sync method from thread2 2016-03-27T18:08:35.887
thread2 after call 2016-03-27T18:08:40.887

Per above output thread1 call to syncMethod acquired the lock on ThreadLockDemo instance 'demo' first. This call starts executing the method almost immediately.

During this execution thread2 call to syncMethod is blocked at line: demo.syncMethod("from thread2");

The output might be different on different machines. I'm running windows 10 on a 4 core/8 logical processors machine. But either of the two threads will be blocked during the sleeping time of other.

Intrinsic lock is on object, not on a method

As mentioned before, if a thread has acquired the lock, other threads will be blocked even if they are calling other 'synchronized' methods of the same object. Non-synchronized methods won't be blocked.

public class MultipleSyncMethodsDemo {

    public static void main (String[] args) throws InterruptedException {
        MultipleSyncMethodsDemo demo = new MultipleSyncMethodsDemo();
        Thread thread1 = new Thread(() -> {
            System.out.println("thread1 before call "+ LocalDateTime.now());
            demo.syncMethod1("from thread1");
            System.out.println("thread1 after call "+LocalDateTime.now());
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2 before call "+LocalDateTime.now());
            demo.syncMethod2("from thread2");
            System.out.println("thread2 after call "+LocalDateTime.now());
        });

        thread1.start();
        thread2.start();
    }

    private synchronized void syncMethod1 (String msg) {
        System.out.println("in the syncMethod1 "+msg+" "+LocalDateTime.now());
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private synchronized void syncMethod2 (String msg) {
        System.out.println("in the syncMethod2 "+msg+" "+LocalDateTime.now());
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Output:

thread1 before call 2016-03-27T20:27:09.075
thread2 before call 2016-03-27T20:27:09.075
in the syncMethod1 from thread1 2016-03-27T20:27:09.086
in the syncMethod2 from thread2 2016-03-27T20:27:14.087
thread1 after call 2016-03-27T20:27:14.087
thread2 after call 2016-03-27T20:27:19.088

If we remove 'synchronized' from one of the methods, say syncMethod2 , it won't block at all.

Intrinsic lock are reentrant

Intrinsic lock are acquired on a per-thread basis rather than per-method call basis. Once a thread has acquired the lock it can internally call other methods without reacquiring the lock. The Lock will only be release when the thread is done with the entry method invocation.

public class ReentrantDemo {

    public static void main (String[] args) throws InterruptedException {
        ReentrantDemo demo = new ReentrantDemo();
        Thread thread1 = new Thread(() -> {
            System.out.println("thread1 before call "+ LocalDateTime.now());
            demo.syncMethod1("from thread1");
            System.out.println("thread1 after call "+LocalDateTime.now());
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2 before call "+LocalDateTime.now());
            demo.syncMethod2("from thread2");
            System.out.println("thread2 after call "+LocalDateTime.now());
        });

        thread1.start();
        thread2.start();
    }

    private synchronized void syncMethod1 (String msg) {
        System.out.println("in the syncMethod1 "+msg+" "+LocalDateTime.now());
        syncMethod2("from method syncMethod1, reentered call");
    }

    private synchronized void syncMethod2 (String msg) {
        System.out.println("in the syncMethod2 "+msg+" "+LocalDateTime.now());
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Output:

thread1 before call 2016-03-27T20:53:21.373
thread2 before call 2016-03-27T20:53:21.373
in the syncMethod1 from thread1 2016-03-27T20:53:21.384
in the syncMethod2 from method syncMethod1, reentered call 2016-03-27T20:53:21.384
thread1 after call 2016-03-27T20:53:24.385
in the syncMethod2 from thread2 2016-03-27T20:53:24.385
thread2 after call 2016-03-27T20:53:27.386

static method synchronization

For static methods we don't need instance. The intrinsic lock is on class level in that case:

public class ThreadStaticSyncDemo {

    public static void main (String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            System.out.println("thread1 before call "+ LocalDateTime.now());
            syncMethod("from thread1");
            System.out.println("thread1 after call "+LocalDateTime.now());
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2 before call "+LocalDateTime.now());
            syncMethod("from thread2");
            System.out.println("thread2 after call "+LocalDateTime.now());
        });

        thread1.start();
        thread2.start();
    }

    private static synchronized void syncMethod (String msg) {
        System.out.println("in the sync method "+msg+" "+LocalDateTime.now());
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

    thread1 before call 2016-03-27T20:59:32.326
    thread2 before call 2016-03-27T20:59:32.326
    in the sync method from thread1 2016-03-27T20:59:32.335
    in the sync method from thread2 2016-03-27T20:59:37.336
    thread1 after call 2016-03-27T20:59:37.336
    thread2 after call 2016-03-27T20:59:42.336


Thread Starvation

Thread starvation occurs when one or more threads are constantly blocked to access a shared resource in favor of other threads.

Thread starvation can occur as a result of assigning inappropriate priorities to different threads or it could be the result of wrong synchronized block logic.

Example Project

Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.0.4

Java Intrinsic Lock Example Select All Download
  • java-thread-intrinsic-lock
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also