首页 新闻 会员 周边

多线程题目求助

0
[已解决问题] 解决于 2023-08-29 21:01

1115题 交替打印FooBar(https://leetcode.cn/problems/print-foobar-alternately/description/)

打算使用2个volatile boolean变量控制多线程逻辑,但是实际运行时总会卡死在while循环。
为什么使用2个volatile变量不可以呢?
volatile机制下,不会指令重排序。但是仍然会卡死在while循环,大佬们有人知道为什么吗?

class FooBar {
    private int n;

    public FooBar(int n) {
        this.n = n;
    }
    private volatile boolean flag1 = false;
    private volatile boolean flag2 = true;
    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            while(flag1){}
            // printFoo.run() outputs "foo". Do not change or remove this line.
            printFoo.run();
            flag1 = true;
            flag2 = false;

        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            while(flag2) {}
            // printBar.run() outputs "bar". Do not change or remove this line.
            printBar.run();
            flag2 = true;
            flag1 = false;
        }
    }
}
谷峦坚的主页 谷峦坚 | 菜鸟二级 | 园豆:202
提问于:2023-08-29 17:29
< >
分享
最佳答案
0

问题可能出在你的 while 循环上。

在你的 foo 方法中,你使用了 while(flag1){} 来等待 flag1 变为 false。同样,在 bar 方法中,你使用了 while(flag2){} 来等待 flag2 变为 false。这种等待的方式被称为"忙等待",会导致线程持续占用 CPU 资源,可能会导致卡死的情况。

为了解决这个问题,你可以考虑使用 wait()notify() 方法来实现线程之间的协调。这样可以避免忙等待,让线程在需要等待的时候释放 CPU 资源,直到被唤醒。

下面是一个使用 wait()notify() 的修改示例:

class FooBar {
    private int n;
    private boolean flag = false;

    public FooBar(int n) {
        this.n = n;
    }

    public synchronized void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            while (flag) {
                wait();
            }
            // printFoo.run() outputs "foo". Do not change or remove this line.
            printFoo.run();
            flag = true;
            notifyAll();
        }
    }

    public synchronized void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            while (!flag) {
                wait();
            }
            // printBar.run() outputs "bar". Do not change or remove this line.
            printBar.run();
            flag = false;
            notifyAll();
        }
    }
}

在修改后的代码中,使用 synchronized 关键字来保证 foobar 方法的互斥执行。当条件不满足时,线程会调用 wait() 方法来等待,并释放锁,让其他线程有机会执行。当条件满足时,线程会调用 notifyAll() 方法来唤醒其他等待的线程。

奖励园豆:5
lanedm | 老鸟四级 |园豆:2381 | 2023-08-29 19:32

感谢评论。

谷峦坚 | 园豆:202 (菜鸟二级) | 2023-08-29 20:36

@谷峦坚: 单核情况下,忙等待导致的。解决方法也很简单,就是在while循环中Thread.yield让出时间片即可。

谷峦坚 | 园豆:202 (菜鸟二级) | 2023-08-29 21:02
其他回答(1)
0

试试 AtomicBoolean
https://zhuanlan.zhihu.com/p/87614101
仅供参考
博文俺没细看,,

快乐的凡人721 | 园豆:3955 (老鸟四级) | 2023-08-29 18:27

感谢评论,虽然无法解决我的问题。

支持(0) 反对(0) 谷峦坚 | 园豆:202 (菜鸟二级) | 2023-08-29 20:34

@谷峦坚:
可以解决这个题的。只不过,不是很理想、优雅的解决方案。
源码:
class FooBar {
private int n;

private AtomicBoolean ab;

public FooBar(int n) {
    this.n = n;
    this.ab = new AtomicBoolean(false);
}

public void foo(Runnable printFoo) throws InterruptedException {
    
    for (int i = 0; i < n; i++) {
        while (! ab.compareAndSet(false, true)) {
            // nothing
        }

    	// printFoo.run() outputs "foo". Do not change or remove this line.
    	printFoo.run();

        try {
            TimeUnit.NANOSECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

public void bar(Runnable printBar) throws InterruptedException {
    
    for (int i = 0; i < n; i++) {
        // 循环等待
        while (! ab.compareAndSet(true, false)) {
            // nothing
        }
        
        // printBar.run() outputs "bar". Do not change or remove this line.
    	printBar.run();

        // 延迟
        try {
            TimeUnit.NANOSECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

}

支持(0) 反对(0) 快乐的凡人721 | 园豆:3955 (老鸟四级) | 2023-08-30 04:03

@快乐的凡人721: 我之前表述不清,我疑惑的是为什么我的解法不行,而不是这个问题的解法。

支持(0) 反对(0) 谷峦坚 | 园豆:202 (菜鸟二级) | 2023-08-30 17:18
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册