Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

Threading in Java: Object Locks - III (Why Object Lock is Not Working?)

4.80/5 (2 votes)
14 Jun 2021CPOL2 min read 4.2K  
Why is Object Lock not Working? This is in continuance with my previous article on Threading in Java: Object Locks - II
This series of articles makes an attempt to help readers with the concept of locks in Java. It is assumed that the reader has some prior knowledge of Java and threads creation in Java.

Introduction

This article is in continuance with the previous article "Threading in Java: Object Locks II". In the previous example, we had created threads t1 and t2 out of the same processor object p1 and synchronized them with an object lock objLock which was a member variable of class processor and instantiated as a part of p1. Thus both threads t1 and t2 could access the same objLock because both threads t1 and t2 were created out of p1 as shown in the below code snippet:

C#
processor p1 = new processor();
Thread t1 = new Thread(p1, "t1");
Thread t2 = new Thread(p1, "t2");

Let's squeeze some lemon to add a tangy taste to this program. Think what would happen if we created t2 out of a new object p2 instantiated out of processor? The program would look like this:

C#
class processor implements Runnable{
    Object obj;
    public processor(){
        obj = new Object();
    }
    public void run() {
        display(); 
    }
    public void display() {
        synchronized(obj) {
            for (int i = 0 ; i<= 5; i++) {
                System.out.println("i = " + i + " In thread" + Thread.currentThread());
            }
        }   
    }
} 

class locks {
    public static void main(String[] args) {
        processor p1 = new processor();
		processor p2 = new processor();
        Thread t1 = new Thread(p1, "t1");
        Thread t2 = new Thread(p2, "t2");
        t1.start();
        t2.start();
    }
}

Can you guess what would be the output of the program? Would it be a regular output or would it be an irregular interleaved output full of printfs from t1 and then t2 and then t1 and so on and so forth?

Well... the above program compiles and runs successfully, but check what output we got!

C#
i = 0 In threadThread[t2,5,main]
i = 0 In threadThread[t1,5,main]
i = 1 In threadThread[t1,5,main]
i = 1 In threadThread[t2,5,main]
i = 2 In threadThread[t1,5,main]
i = 2 In threadThread[t2,5,main]
i = 3 In threadThread[t1,5,main]
i = 3 In threadThread[t2,5,main]
i = 4 In threadThread[t1,5,main]
i = 4 In threadThread[t2,5,main]
i = 5 In threadThread[t1,5,main]
i = 5 In threadThread[t2,5,main]

If you observe carefully, we got an interleaved, irregular output. Well, if we had an object lock, why did we get an interleaved output? The answer to this is simple. Well, we do have an object lock. But in this case, the objectLock for p1 and objectLock for p2 are different and not common. The object p1 created out of processor() has its own instance of objectLock which is totally different from objectLock of p2 created out of processor() because p1 and p2 are two different objects now. There is no race condition to acquire a same lock. Thread t2 acquires its own lock and thread t1 acquires its own lock and they proceed. Hence, we get an interleaved output.

So what do we do to get a synchronized output? In order to get a synchronized output, we need to ensure that both the threads have access to the same lock. This is done in the following example:

C#
class processor implements Runnable{
    Object objLock;
    public processor(Object objLock){
        this.objLock = objLock;
    }
    public void run() {
        display(); 
    }
    public void display() {
        synchronized(objLock) {
            for (int i = 0 ; i<= 5; i++) {
                System.out.println("i = " + i + " In thread" + Thread.currentThread());
            }
        }   
    }
} 

class locks {
    public static void main(String[] args) {
        Object objLock = new Object();
        processor p1 = new processor(objLock);
        processor p2 = new processor(objLock);
        Thread t1 = new Thread(p1, "t1");
        Thread t2 = new Thread(p2, "t2");
        t1.start();
        t2.start();
    }
}

Output
=======
i = 0 In threadThread[t1,5,main]
i = 1 In threadThread[t1,5,main]
i = 2 In threadThread[t1,5,main]
i = 3 In threadThread[t1,5,main]
i = 4 In threadThread[t1,5,main]
i = 5 In threadThread[t1,5,main]
i = 0 In threadThread[t2,5,main]
i = 1 In threadThread[t2,5,main]
i = 2 In threadThread[t2,5,main]
i = 3 In threadThread[t2,5,main]
i = 4 In threadThread[t2,5,main]
i = 5 In threadThread[t2,5,main]

The above program compiles and runs successfully to produce the desired synchronized output. For thread t1 and t2 to have the same objLock, we created it in main() and then passed it to processor objects p1 and p2 through its constructors. Now both p1 and p2 share the same objLock. During the race condition in the display() method for the forloop, t1 acquires the lock to objLock. Hence t2 has to wait until t1 releases the lock. t2 gets an opportunity to enter the forloop only after t2 has released the lock. This way, we get a synchronized output. Hope that makes this clear on how object locks are working. Two or more threads will synchronize if they have one and only one object lock in common.

Points of Interest

Two or more threads will synchronize if they have one and only one object lock in common.

History

  • 15th June, 2021: Initial revision

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)