john john - 3 months ago 9
Java Question

Does a synchronized method have to be external to a class that's using it?

Scenario 1.

A synchronized method is private and located within a class that implements Runnable

Main.java

public class Main {

Thread thread1 = new Thread(new MyRunnable);

. . .

}


MyRunnable.java

public class MyRunnable implements Runnable {

. . .

private synchronized doSomething {

}





Scenario 2.

A synchronized method is public and static and located in the Main class

Main.java

public class Main {

Thread thread1 = new Thread(new MyRunnable);

public synchronized static doSomething() {

}

}


MyRunnable.java

public class MyRunnable implements Runnable {

. . .


}


Question: which of the above scenarios is correct ?

I'm following scenario 2 . So I have a synchronized method in the Main class. This works fine. When i moved this synchronized method to the MyRunnable class, I didn't see any difference. And this is odd. I was expecting it to fail .
synchronized
prevents from accessing this method simultaneously.

But if I instanciate two instances of a class :

MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();


Each of this runnables will have it's own
synchronized
, and
synchronized
will have no meaning to the compiler. Do I understand it correctly ?

Answer

synchronized defines runtime behavior. It doesn't do anything at compile-time.

A synchronized method is locked on the instance of the object (this).
A static synchronized method is locked on the Class of the object.

This behavior is defined in JLS §8.4.3.6. synchronized Methods

A synchronized method acquires a monitor (§17.1) before it executes.

For a class (static) method, the monitor associated with the Class object for the method's class is used.

For an instance method, the monitor associated with this (the object for which the method was invoked) is used.

So, in your Scenario 1 the method is locked for each individual instance of MyRunnable, and in Scenario 2 the method is locked on the Main.class object (which is more or less global, its the same when under the same class loader).

Consider this, in *Scenario 1:

MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);

Here, both t1 and t2 use the same instance of MyRunnable, which means they won't be able execute doSomething() in parallel.

Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());

Here, t1 and t2 can execute doSomething() on the instance of MyRunnable they were given in paralell, since they lock on the instance.

In Scenario 2, both threads can not execute doSomething() in paralell, because they lock in Main.class and are loaded by the same ClassLoader.