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;
}
}
}
问题可能出在你的 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
关键字来保证 foo
和 bar
方法的互斥执行。当条件不满足时,线程会调用 wait()
方法来等待,并释放锁,让其他线程有机会执行。当条件满足时,线程会调用 notifyAll()
方法来唤醒其他等待的线程。
感谢评论。
@谷峦坚: 单核情况下,忙等待导致的。解决方法也很简单,就是在while循环中Thread.yield让出时间片即可。
试试 AtomicBoolean
https://zhuanlan.zhihu.com/p/87614101
仅供参考
博文俺没细看,,
感谢评论,虽然无法解决我的问题。
@谷峦坚:
可以解决这个题的。只不过,不是很理想、优雅的解决方案。
源码:
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);
}
}
}
}
@快乐的凡人721: 我之前表述不清,我疑惑的是为什么我的解法不行,而不是这个问题的解法。