吃面死锁问题

x33g5p2x  于2022-04-17 转载在 其他  
字(3.1k)|赞(0)|评价(0)|浏览(180)

一 点睛

虽然 synchronized 关键字可以保证 single thread execution,但是如果使用不当就会导致发生死锁,比如 A 手持刀等待 B 放下叉,而 B 手持叉等待 A 放下刀。

下看看下面死锁代码。

二 死锁代码

1 Tableware 类

package concurrent.eatnoodle;

/**
* @className: Tableware
* @description: 餐具类
* @date: 2022/4/14
* @author: cakin
*/
public class Tableware {
    // 餐具名称
    private final String toolName;

    public Tableware(String toolName) {
        this.toolName = toolName;
    }

    @Override
    public String toString() {
        return "Tool:" + toolName;
    }
}

2 EatNoodleThread

package concurrent.eatnoodle;

public class EatNoodleThread extends Thread {
    private final String name;

    // 左手边的餐具
    private final Tableware leftTool;

    // 右手边的餐具
    private final Tableware rightTool;

    public EatNoodleThread(String name, Tableware leftTool, Tableware rightTool) {
        this.name = name;
        this.leftTool = leftTool;
        this.rightTool = rightTool;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            this.eat();
        }
    }

    private void eat() {
        synchronized (leftTool) {
            System.out.println(name + " take up " + leftTool + "(left)");
            synchronized (rightTool) {
                System.out.println(name + " take up " + rightTool + "(right)");
                System.out.println(name + " is eating now.");
                System.out.println(name + " put down " + rightTool + "(right)");
            }
            System.out.println(name + " put down " + leftTool + "(left)");
        }
    }
}

3 测试类

package concurrent.eatnoodle;

public class Test1 {
    public static void main(String[] args) {
        Tableware fork = new Tableware("fork");
        Tableware knife = new Tableware("knife");
        new EatNoodleThread("A", fork, knife).start();
        new EatNoodleThread("B", knife, fork).start();
    }
}

三 解决吃面引起的死锁

1 将刀叉进行封装,使得刀叉在同一个类

package concurrent.eatnoodle;

public class TablewarePair {
    private final Tableware leftTool;
    private final Tableware rightTool;

    public TablewarePair(Tableware leftTool, Tableware rightTool) {
        this.leftTool = leftTool;
        this.rightTool = rightTool;
    }

    public Tableware getLeftTool() {
        return leftTool;
    }

    public Tableware getRightTool() {
        return rightTool;
    }
}

2 在线程类中,将 TablewarePair 代替  leftTool 和 rightTool,这样就可以避免交叉锁。

package concurrent.eatnoodle;

public class EatNoodleThread1 extends Thread {
    private final String name;

    private final TablewarePair tablewarePair;

    public EatNoodleThread1(String name, TablewarePair tablewarePair) {
        this.name = name;
        this.tablewarePair = tablewarePair;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            this.eat();
        }
    }

    private void eat() {
        synchronized (tablewarePair) {
            System.out.println(name + " take up " + tablewarePair.getLeftTool() + "(left)");
            System.out.println(name + " take up " + tablewarePair.getRightTool() + "(right)");
            System.out.println(name + " is eating now.");
            System.out.println(name + " put down " + tablewarePair.getRightTool() + "(right)");
            System.out.println(name + " put down " + tablewarePair.getLeftTool() + "(left)");
        }
    }
}

3 测试代码

package concurrent.eatnoodle;

public class Test2 {
    public static void main(String[] args) {
        Tableware fork = new Tableware("fork");
        Tableware knife = new Tableware("knife");

        TablewarePair tablewarePair = new TablewarePair(fork, knife);

        new EatNoodleThread1("A", tablewarePair).start();
        new EatNoodleThread1("B", tablewarePair).start();
    }
}

4 运行结果

不发生死锁,正常运行。

5 说明

在 Single Thread Execute 中,synchronized 关键字起到决定性的作用,但是 synchronized 的排他性是以性能的牺牲为代价的,因此在保证线程安全的前提下应尽量缩小 synchronized 的作用域。

相关文章