I have two classes:
Deadlock1.java
class Client {
final Object resource1 = "resource1";
final Object resource2 = "resource2";
void doS1() {
synchronized(resource1) {}
}
void doS2() {
synchronized(resource2) {}
}
}
public class Deadlock1 {
public static void main(String[] args) {
Client client = new Client();
new Thread(
() ->
{
client.doS1();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
client.doS2();
}).start();
new Thread(
() ->
{
client.doS2();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
client.doS1();
}).start();
}
}
and Deadlock2.java
class Client {
final Object resource1 = "resource1";
final Object resource2 = "resource2";
}
public class Deadlock2{
public static void main(String[] args) {
Client client = new Client();
new Thread(
() ->
{
synchronized (client.resource1) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
synchronized (client.resource2) {}
}
}).start();
new Thread(
() ->
{
synchronized (client.resource2) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
synchronized (client.resource1) {}
}
}).start();
}
}
In Deadlock1 deadlock didn't happened but in Deadlock2 did. I don't understand why? And i do not quite understand the meaning of the concept of synchronized block. Why this block is part of thread code but not some common piece of code which different threads execute?
A synchronized block prevents simultaneous execution of code on the same monitor object. In this case of doS1()
the monitor object is resource1
and in doS2() the monitor object is resource2
. When a thread enters a synchronized block it attempts to acquire a lock on the monitor object. If it gets the lock it will proceed and release the lock when only when it exits the block (or it releases the lock). If it can't get the lock (because another thread has the lock already then the thread will block until the lock is released and it can acquire it).
Your two examples above, Deadlock1
and Deadlock2
, are not executing equivalent code. In Deadllock1
both monitor objects locks can't be acquired by the same the same thread at the same time.
In Deadlock2
each thread it trying to acquire a lock on both monitor objects at the same time.
If we rewrite Deadlock1
to mimic the functionality of Deadlock2
so that it does produce a deadlock it would look like this:
public class Deadlock1 {
static class Client {
final Object resource1 = "resource1";
final Object resource2 = "resource2";
void doS1() {
synchronized (resource1) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
doS2();
}
}
void doS2() {
synchronized (resource2) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
doS1();
}
}
}
public static void main(String[] args) {
Client client = new Client();
new Thread(client::doS1).start();
new Thread(client::doS2).start();
}
}
Alternatively if we rewrite Deadlock2
to mimic the functionality of Deadlock1
so it doesn't produce a deadlock it would look like this:
public class Deadlock2 {
static class Client {
final Object resource1 = "resource1";
final Object resource2 = "resource2";
}
public static void main(String[] args) {
Client client = new Client();
new Thread(
() ->
{
synchronized (client.resource1) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
synchronized (client.resource2) {}
}).start();
new Thread(
() ->
{
synchronized (client.resource2) {
try {
System.out.println("3");
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
synchronized (client.resource1) {}
}).start();
}
}