Search code examples
javathread-safetyjava-threadsthread-synchronizationsynchronization

Java: thread able to call a method that is in synchronised block of some other thread


thread t1 is calling test1() method of Test class object ob.
thread t2 is calling test1() method of Test class object ob in synchronized block.
t1 is able to call test1() method of ob even though test1() method call of ob is in synchronised block of thread t2.

The code is given below:

class Test {
    void test1() {
        while(1 == 1) {
            System.out.println(Thread.currentThread().getName() + " test1!");
        }
    }

    void test2() {
        while(1 == 1) {
            System.out.println(Thread.currentThread().getName() + " test2!");
        }
    }
}

class NewThread1 implements Runnable {
    Thread t;
    String name;
    Test target;

    NewThread1(Test ob, String threadname) {
        target = ob;
        name = threadname;
        t = new Thread(this, name);
    }

    public void run() {
        target.test1();
    }
}

class NewThread2 implements Runnable {
    Thread t;
    String name;
    Test target;

    NewThread2(Test ob, String threadname) {
        target = ob;
        name = threadname;
        t = new Thread(this, name);
    }

    public void run() {
        synchronized(target) {
            target.test1();
        }
    }
}

class Test1 {
    public static void main(String args[]) {
        Test ob = new Test();
        NewThread1 t1 = new NewThread1(ob, "t1");
        NewThread2 t2 = new NewThread2(ob, "t2");

        t2.t.start();
        t1.t.start();

        try {
            t1.t.join();
            t2.t.join();
        } catch(InterruptedException e) {
            System.out.println("Main thread interrupted");
        }

        System.out.println("Main thread exiting");
    }
}

Solution

  • Since NewThread1#run() is not synchronized it will not try to get the monitor on the target and it will therefore not be blocked, it can call the method on the target even if another thread holds the monitor of it.

    Synchronized can only exclusively lock out other threads if all threads compete against the same monitor with a synchronized section. (It does not matter of you call test1 or test2 the check happens in the synchronize based on the target). What you could do is to make test1 and test2 synchronized methods, then they will try to reserve the monitor of the instance in all cases). Same is not only true for exclusive execution, but also for any memory access guarantees (happens-after) you might want to get out of a synchronized block.

    BTW you don’t need different thread classes, if you only use one (the one with the synchronized) it works like expected.

    Thread t1 = new NewThread2(ob, "t1");
    Thread t2 = new NewThread2(ob, "t2");
    

    However if your scope of locking is narrow, it is much better to localize the locking inside (all) instance methods of the target Test, because then you can never call them with a missing synchronized (and you can switch to other locking primitives without the caller having to know).

    void synchronized test1() {
        while(1 == 1) {
            System.out.println(Thread.currentThread().getName() + " test1!");
        }
    }
    

    Or

    void test1() {
        synchronized(this) {
            while(1 == 1) {
                System.out.println(Thread.currentThread().getName() + " test1!");
            }
        }
    }