如何在Java中预防简单死锁?

3

我有一个简单的例子,说明如何导致死锁:

class Player {
    private String name;

    public Player(String name) {
        super();
        this.name = name;
    }

    public synchronized void passTo(Player p) {
        System.out.println(this.name + " passes to " + p.name);
        // to imitate long task
        for (int i = 0; i < 1000000000; i++)
            ;
        p.passBack(this);
    }

    private synchronized void passBack(Player p) {
        System.out.println(this.name + " passes back to " + p.name);
    }
}

public class Deadlock {

    public static void main(String[] args) {
        final Player ivan = new Player("Ivan");
        final Player petro = new Player("Petro");

        new Thread(new Runnable() {
            public void run() {
                ivan.passTo(petro);
            }
        }).start();
        new Thread(new Runnable() {
            public void run() {
                petro.passTo(ivan);
            }
        }).start();
    }
}

当我运行这个程序时,它会导致死锁。

有哪些可能的解决方案可以防止这种简单的死锁发生?

谢谢!


没有使用synchronized关键字吗?在您的情况下,您只有1个球。因此,在第二次传球之前,第一次传球必须完成。这是正确的做法。为了使其无缝,请减少调用它的次数。或者减少所需的时间。目前似乎已经做到了正确的事情。 - exussum
为什么需要在synchronized中使用passBack?它是私有的,只从passTo调用。 - morgano
演示的目的是展示死锁如何发生。 - Volodymyr Levytskyi
你的代码中没有死锁。要出现死锁,你的 passBack() 方法必须调用 passTo() 方法。然后这两个方法会互相等待。在你的情况下,你总是先调用 passTo() 方法,所以永远不会出现死锁。 - sergej shafarenka
2个回答

1

您需要锁定与类的对象无关的对象,它是 class 本身。

要在 class 本身上获取锁定,您还必须使该方法 static 并且加上 synchronized

示例代码:(根据您的要求调整代码)

class Player {
    private String name;

    public Player(String name) {
        super();
        this.name = name;
    }

    public static synchronized void passTo(Player to, Player from) {
        System.out.println(from.name + " passes to " + to.name);
        Player.passBack(from, to);
    }

    private static synchronized void passBack(Player from, Player to) {
        System.out.println(from.name + " passes back to " + to.name);
    }

}

final Player ivan = new Player("Ivan");
final Player petro = new Player("Petro");

new Thread(new Runnable() {
    public void run() {
        Player.passTo(petro, ivan);
    }
}).start();
new Thread(new Runnable() {
    public void run() {
        Player.passTo(ivan, petro);
    }
}).start();

输出:

Ivan passes to Petro
Ivan passes back to Petro
Petro passes to Ivan
Petro passes back to Ivan

1
你可以将 public synchronized void passTo(Player p) 方法改为静态方法。这样一次只有一个玩家可以调用 passTo() 方法,就不会出现死锁,即解除循环依赖关系。所以把它改成: public static synchronized void passTo(Player p)

thispassTo()passBack() 方法中被使用。 - Braj
这就是为什么获取的锁将会是实例级别的锁,因此会导致死锁。为了解决这个问题,你需要获取类级别的锁。这样即使有两个Player实例,也只会有一个在任何时候获得锁,避免死锁发生。 - Aniket Thakur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接