简体   繁体   中英

Why no deadlock on the same object in this program - Java Multithreading

I have three classes below. Main, two threads and Obj whose methods are synchronized

public class DeadLocks {

    public static void main(String[] args) {
        SyncObj so = new SyncObj();
        ThreadObj to = new ThreadObj(so);
        ThreadObj1 to1 = new ThreadObj1(so);

        to.start();
        to1.start();
    }
}

class SyncObj {
    synchronized void foo() {
        System.out.println("Foo Started");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bar();
    }

    synchronized void bar() {
        System.out.println("bar started");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        foo();
    }

}

class ThreadObj1 extends Thread {
    SyncObj so;

    public ThreadObj1(SyncObj so) {
        this.so = so;
    }

    @Override
    public void run() {
        so.bar();
    }
}

class ThreadObj extends Thread {
    SyncObj so;

    public ThreadObj(SyncObj so) {
        this.so = so;
    }

    @Override
    public void run() {
        so.foo();
    }
}

In the above code I'm calling synchronized methods on the same object. Both the methods are executing and calling each other simultaneously.There is no deadlock situation. Could anyone explain why? sorry for such a silly question.

As far as I can see, you are using the same object ( so ) for both the cases. So there is no case for a deadlock. You would need to lock on two or more objects wherein each critical section requires the lock other than the one that it is holding. That other lock is held by "another" thread.

Confusing, this will enlighten: " https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html "

You would never ever deadlock in a scenario that you describe. In this scenario there is only one so that is being shared and is being synchronized with.

Let me give you an example to illustrate:

Suppose Andy and Sandy are playing with two soccer balls B1 and B2 .

Now further suppose that both Andy and Sandy have balls B1 and B2 in their possession respectively. For example Andy has ball B1 and Sandy has ball B2 .

Now they devise a game wherein they need both the balls each. Now Sandy wants ball B1 as well and at the same time, Andy wants B2 .

And both of them cannot relinquish the balls that they hold. Andy will not give up B1 and vice-versa.

So both of them cannot continue and are stuck. We call this a deadlocked situation.

Hope this example helps. You could use your imagination to increase the number of balls in play to 3 or 4 (and so on..) and / or increase the number of players.

You do not get a deadlock because your program does not meet two of the four necessary conditions of forming it:

  • mutual exclusion - yes,
  • hold on partial allocation - no,
  • no pre-emption - yes,
  • circular dependency - no

You need at least two resources, "A" and "B", to form a deadlock. One thread should grab "A" and attempt to grab "B", while the other should grab "B", and attempt to grab "A".

I'm not sure why you expect a deadlock here. It's true that only one of objects has an access to synchronized section, but then it has this access as much as it wants. You can modify the code to make it clear:

public class DeadLocks {

     public static void main(String[] args) {
         SyncObj so = new SyncObj();
         ThreadObj to = new ThreadObj(so);
         ThreadObj1 to1 = new ThreadObj1(so);

         to.start();
         to1.start();
     }
 }

 class SyncObj {
     synchronized void foo(String msg) {
         System.out.println("Foo Started: " + msg);
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         bar(msg);
     }

     synchronized void bar(String msg) {
         System.out.println("bar started: " + msg);
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         foo(msg);
     }

 }

 class ThreadObj1 extends Thread {
     SyncObj so;

     public ThreadObj1(SyncObj so) {
         this.so = so;
     }

     @Override
     public void run() {
         so.bar("TO1");
     }
 }

 class ThreadObj extends Thread {
     SyncObj so;

     public ThreadObj(SyncObj so) {
         this.so = so;
     }

     @Override
     public void run() {
         so.foo("TO");
     }
 }

You can see the following output:

Foo Started: TO
bar started: TO
Foo Started: TO
bar started: TO
Foo Started: TO
bar started: TO

You can consider 'synchronized' to mean lock(this), where 'this' is a SyncObj instance. Therefore there is exactly one lock, and it's impossible to obtain a deadlock.

While others have already pointed out that a deadlock only occurs when you have two resources where each thread locks one and then waits for the other, I think there is one crucial point missing, where your entire confusion probably comes from:

synchronize on a method does not create a lock for that particular method, it creates a lock for the entire object it belongs to. Thus your class is equivalent to this:

class SyncObj {
    void foo() {
        synchronized(this) {
            System.out.println("Foo Started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bar();
        }
    }

    void bar() {
        synchronized(this) {
          System.out.println("bar started");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          foo();
        }
    }
}

Now it should be much more clear, why you don't get a deadlock.

You can easily modify your code to be prone to a deadlock by introducing two resources, one for each method:

class SyncObj {
    private Object foolock = new Object();
    private Object barlock = new Object();

    void foo() {
        synchronized(foolock) {
            System.out.println("Foo Started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bar();
        }
    }

    void bar() {
        synchronized(barlock) {
          System.out.println("bar started");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          foo();
        }
    }
}

Deadlock is not possible using a single instance because no two threads of the same object can access more than one synchronised method. In the above example if thread 1 is accessing foo method. Thread 2 cannot access either foo nor bar method. Untill thread 1 finishes it task

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM