Search code examples
javamultithreadingsynchronizationdeadlocksynchronized

What happens when two locked threads (by a variable), switch one of their variable and try to access another?


(I just started threads, so there is a big chance I'll be wrong)

If two threads of the same class, one of their variables gets into a synchronized method, and they switch one of their variables, and try to access another synchronized method using a switched variable. a deadlock will occur? and why?

An example of what I'm talking about: The class:

public class Person extends Thread{
    public Hand leftHand;
    public Hand rightHand;
    public String name;
    public Person friend;

    public Person(Hand a, Hand b, String name, Person f){
        this.leftHand = a;
        this.rightHand = b;
        this.name = name;
        this.friend = f;
    }

    public void switchHands(){
        if(leftHand.color.equals(rightHand.color)){
            Hand temp = this.rightHand;
            this.rightHand = friend.rightHand;
            friend.rightHand = temp;
        }
    }

    public void run(){
        synchronized (leftHand){
            System.out.println(this.name + " locked with " + this.leftHand);
            switchHands();
            synchronized (rightHand){
                 System.out.println(this.name + " locked with " + this.rightHand);   
            }
        }
    }

    public static class Hand{
        String color;
        public Hand(String c){ this.color = c; }  
    }
}

And in the main:

public static void main(String[] args){
    Hand whiteHand = new Hand("white");
    Hand blackHand = new Hand("black");
    Person one = new Person(whiteHand, whiteHand, "one", null);
    Person second = new Person(blackHand, blackHand, "second", null);
    one.friend = second;
    second.friend = one;
    one.start();
    second.start()
}

as you can see, the two threads (one and two), gets locked in synchronized (leftHand) , afterwards one of the threads at least switch hands (the right hand), and then - we try to access synchronized (rightHand), and a deadlock probably occurred.

I can understand the logic, but when I switch the hands, intuitively I think I'll just copy the content of the other hand, but I don't understand why a deadlock occurs.

P.S I think my title is not precise enough, so an edit will be welcome.


Solution

  • Don't think about your code as synchronizing on leftHand or rightHand because that's not what it is doing. You're really synchronizing on either whiteHand or blackHand. Your two different Person objects, when run, could look something like this:

    Person one: synchronized whiteHand
    Person two: synchronized blackHand
    Person two: synchronized whiteHand
    Person one: synchronized blackHand
    

    Can you see how this will not work? You have not prevented the other thread from synchronizing on the inner synchronized block. It is possible for Person two to acquire the blackHand lock, and then wait for Person one to release the whiteHand lock. However, Person one won't release the already held whiteHand lock, because it is waiting for Person two to release the blackHand lock, which in turn is waiting for Person one and so on and so forth. This cyclic dependency will lead to deadlock.

    The quick fix here is to simply use a lock per instance of Person and make Hand thread safe by setting the color to final.

    After that, you'd need to set name to final and synchronize access to friend in order to make Person thread safe.