Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java: can synchronized block interleave?

I have an unexpected (for me at least) output with this code

public class Test {
    static boolean condition = false;

    void runme() {
        var reader = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("waiting for condition");
                    while (!condition) {}
                    System.out.println("condition is true");
                }
            }
        };

        var writer = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("condition is set to true");
                    condition = true;
                }
            }
        };

        new Thread(reader).start();
        new Thread(writer).start();

    }

    public static void main(String[] args) {
        new Test().runme();
    }
}

Based on the documentation, I expected a deadlock if the reader object starts first, since

  1. it acquires the lock for this (entering the synchronized block)
  2. prints "waiting for condition"
  3. get stuck in the infinite loop forever
  4. the other thread waits for the this lock, to get into its own synchronized block

However, on some runs of the code I get the output

waiting for condition
condition is set to true
condition is true

Am I missing something or have I misunderstood how synchronized blocks/methods work?

like image 872
blue_note Avatar asked Jan 02 '23 09:01

blue_note


1 Answers

The two synchronized (this) statements reference the Runnable anonymous classes.
So the synchronizations of the two Runnable instances don't operate on the same lock.
You have to synchronize on the outer class instance to lock on the same monitor such as :

synchronized (Test.this) {...}

Additionally, note that by using a lambda to implement the Runnable functional interface such as :

var writer = () -> {
    synchronized (this) {
        System.out.println("condition is set to true");
        condition = true;
    }
};

you could keep the actual syntax (synchronized (this)) as this in this case doesn't refer the anonymous class that doesn't exist but refers the outer instance.

like image 103
davidxxx Avatar answered Jan 11 '23 22:01

davidxxx