我有两节课:
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();
}
}
和 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();
}
}
在Deadlock1中没有发生死锁,但在Deadlock2中却发生了。我不明白为什么?而且我不太了解同步块概念的含义。为什么此块是线程代码的一部分,而不是不同线程执行的一些通用代码?
答案 0 :(得分:2)
同步块阻止在同一监视对象上同时执行代码。在doS1()
的情况下,监视对象为resource1
,在doS2()中,监视对象为resource2
。当线程进入同步块时,它将尝试获取监视对象上的锁。如果它获得了锁,它将继续并仅在它退出该块时释放该锁(或释放该锁)。如果无法获取该锁(因为另一个线程已经拥有该锁,则该线程将阻塞直到释放该锁并可以获取它为止)。
您上面的两个示例Deadlock1
和Deadlock2
没有执行等效的代码。在Deadllock1
中,两个监视对象锁不能由同一线程同时获得。
在Deadlock2
的每个线程中,它试图同时获取两个监视对象的锁。
如果我们重写Deadlock1
来模仿Deadlock2
的功能,使其确实产生死锁,它将看起来像这样:
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();
}
}
或者,如果我们重写Deadlock2
来模仿Deadlock1
的功能,以免产生死锁,它将看起来像这样:
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();
}
}
答案 1 :(得分:1)
死锁1:
Thread 1 is acquiring the lock on 'resource1' and immediately releases it.
Thread 1 is waiting for x milliseconds.
Thread 1 is trying to acquire the lock on 'resource2'.
Thread 2 is acquiring the lock on 'resource2' and immediately releases it.
Thread 2 is waiting for x milliseconds.
Thread 2 is trying to acquire the lock on 'resource1'.
由于两个线程永远不会同时拥有相同的资源,所以这不是问题。
死锁2:
Thread 1 is acquiring the lock on 'resource1' and holds it.
Thread 1 is waiting for x milliseconds.
Thread 1 is trying to acquire the lock on 'resource2'.
Thread 2 is acquiring the lock on 'resource2' and and holds it.
Thread 2 is waiting for x milliseconds.
Thread 2 is trying to acquire the lock on 'resource1'.
由于线程1具有resouce1,线程2具有资源2,都无法获得资源,因此导致死锁。
我不太理解“概念”的含义 同步块。为什么此块是线程代码的一部分,而不是一部分 不同线程执行的共同代码?
一个同步块描述了代码的一部分,其中资源(对象引用)上的锁被保持。在该块的开头,将获得锁(否则代码将等到这种情况发生)。当块结束时,锁被释放。
具有关键字synchronized
的方法的行为相同,只是在对象本身上获得了锁定。
维护这些锁的方法有很多种,您当然可以使用两个线程都在使用的通用共享代码。