Reputation: 29099
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
this
(entering the synchronized block)this
lock, to get into its own synchronized blockHowever, 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?
Upvotes: 4
Views: 339
Reputation: 131546
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.
Upvotes: 4
Reputation: 5336
The reference this
refers to the current object. Inside the instance method of an inner class this
refers to the current object of the inner class. In case you want to access the current object of outer enclosing class then you need to use like : OuterClassName.this
.
In your case you two separate objects of have anonymous classes. The synchronized blocks do not share common object to lock upon so actually there is no synchronization rather both the threads run in parallel independently.
Upvotes: 2
Reputation: 11030
In your code, synchronized(this)
refers to two different objects. So neither code blocks the other, they just run simultaneously.
Another way to look at it is to not use var
or local classes. Just declare two top level classes that do the same thing as reader
and writer
:
// var reader = new Runnable() {
class Reader implements 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() {
class Writer implements Runnable {
@Override
public void run() {
synchronized (this) {
System.out.println("condition is set to true");
condition = true;
}
}
}
It should be pretty obvious that this
in this case refers to an instance of each class, not the same object, so these two classes could never block each other.
Upvotes: 3