public class Deadlock {
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public synchronized void bow(Friend bower) {
System.out.format("%s: %s"
+ " has bowed to me!%n",
this.name, bower.getName());
bower.bowBack(this);
}
public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s"
+ " has bowed back to me!%n",
this.name, bower.getName());
}
}
public static void main(String[] args) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}
当我运行这个程序时,我得到的输出为
艾尔冯斯:"加斯顿向我鞠躬了!加斯顿: 艾尔冯斯向我鞠躬了!
那么两个线程可以同时访问同步方法吗?
两个线程可以同时访问同步方法吗?
这取决于两个线程尝试锁定的对象实例。 两个线程无法访问同一对象实例上的同一 synchronized
方法。 一个将获得锁,另一个将阻塞,直到第一个线程离开该方法。
在您的示例中,实例方法在包含它们的对象上同步。 在这种情况下,当您调用alphonse.bow(...)
时,您将锁定alphonse
对象。 gaston.bow(...)
锁gaston
.
几种方法可以让一个对象的多个实例锁定在同一对象上。
您可以使方法
static
并synchronized
在这种情况下,它们将锁定类对象本身。 每个类装入器只有一个这些对象。public static synchronized void bow(Friend bower) {
它们都可以锁定定义的静态对象。 像这样:
private static final Object lockObject = new Object(); ... public void bow(Friend bower) { synchronized (lockObject) { .... } }
- 或者,如果您不想使其静态,则可以传入对象以锁定。
您的输出可能如下所示:
gaston
线程(可能(首先启动并调用bow(alphonse)
- 这将锁定
gaston
对象并输出:Gaston: Alphonse has bowed to me!
- 它调用
alphonse.bowBack(this)
. - 此调用锁定
alphonse
对象并输出:Alphonse: Gaston has bowed back to me!
-
alphonse.bowBack(this)
退出,解锁alphonse
对象。 -
gaston.bow(alphonse)
退出,解锁gaston
对象。 - 然后
gaston
线程退出。 alphonse
线程(可能(接下来开始并调用bow(gaston)
- 这将锁定
alphonse
对象并输出:Alphonse: Gaston has bowed to me!
- 它调用
gaston.bowBack(this)
. - 此调用锁定
gaston
对象并输出:Gaston: Alphonse has bowed back to me!
-
gaston.bowBack(this)
退出,解锁gaston
对象。 -
alphonse.bow(gaston)
退出,解锁alphonse
对象。
这可能以许多不同的顺序发生。 alphonse
线程可以先运行,即使它start()
方法稍后被调用。 锁唯一可以使您免于的是alphonse.bowBack(...)
当前正在运行时调用alphonse.bow(...)
。 正如@user988052指出的那样,由于每个线程都锁定了自己的对象,然后尝试锁定另一个线程,因此很容易出现死锁。
那么两个线程可以同时访问同步方法吗?
是和否:
-
是的,如果在类的不同实例上调用该方法。
-
不可以,两个线程不能同时在类的同一实例上调用同步方法。即使两个线程调用不同的方法(只要实例相同(,情况也是如此。
我没有详细检查您的代码,但我想我认识到有关如何创建死锁的典型示例。
但是,您不应该只调用一次来尝试创建死锁。
在循环中创建线程,您很有可能会出现死锁:
for ( int i = 0; i < 1000; i++ ) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
请注意,您不会死锁 2000 个线程:只有其中一些线程会死锁。 您可以通过获取程序/JVM 的线程来验证这一点。
如死锁教程中所述,这是此代码的来源,此代码通常会阻塞。
当死锁运行时,两个线程在尝试调用 bowBack 时极有可能都会阻塞。两个块都不会结束,因为每个线程都在等待另一个线程退出弓。
使用 sync 关键字,您可以锁定实例方法的实例或静态方法的类。所以在这里你保证最多一个线程在给定时间在给定实例上执行 bow 或 bowBack(如果一个线程执行 bow,则没有其他线程可以执行 bowBack,因为两个方法在同一锁上同步(......
还有一条评论:由于一旦线程获得锁,锁是可重入的,因此它可以进入在同一锁上同步的其他方法。