我试图了解信号量,阅读 Allen B. Downey 的 The Little Book of Semaphores。
其中有一个谜题:
概括集合点解决方案。每个线程都应该运行 以下代码:
1 rendezvous
2 critical point
同步要求是在所有线程都执行完集合点之前没有线程执行临界点。 您可以假设有 n 个线程,并且该值存储在一个变量 n 中,所有线程都可以访问该变量。 当前 n − 1 个线程到达时,它们应该阻塞,直到第 n 个线程到达,此时所有线程都可以继续进行
我的解决方案似乎只有时有效,而在其他情况下,它在打印所有集合点和一些但不是全部关键点后会卡住
我无法弄清楚这里出了什么问题
这是我的代码解决方案 -
import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;
class Main {
private static final int n = 10;
private static int counter = 0;
private static final Semaphore mutex = new Semaphore(1);
private static final Semaphore barrier = new Semaphore(0);
private static class Runner implements Runnable {
@Override
public void run() {
try {
System.out.println("Rendezvous");
mutex.acquire();
counter = counter + 1;
mutex.release();
while (counter < n);
barrier.release();
barrier.acquire();
System.out.println("Critical Section");
barrier.release();
} catch (InterruptedException ex) {
System.out.println("Got exception while executing code for runner : " + ex.getMessage());
}
}
}
public static void main(String[] args) {
List<Thread> threads = new ArrayList<>();
for (int i = 0;i < n;i++) {
Thread t = new Thread(new Runner());
t.start();
threads.add(t);
}
for (Thread t : threads) {
try {
t.join();
} catch (InterruptedException ex) {
System.out.println("Got exception while executing code for runner : " + ex.getMessage());
}
}
System.out.println("Completed");
}
}
代码的问题是:
while (counter < n);
看起来它正在尝试等待,直到计数器增加到 n。问题是当前线程中没有任何事情可以在循环期间增加计数器。出于优化目的,允许当前线程假设没有其他线程正在操作该变量。例如,编译器可以将值缓存在硬件寄存器中,而无需在每次检查比较时返回到共享内存位置。 还有很多其他事情可能会出错。
如果共享 Integer 不受互斥锁保护(就像这里的情况一样),则可以使用 AtomicInteger 强制将值同步到所有线程,并挫败上述优化,如下所示:
mutex.acquire()
counter.incrementAndGet();
mutex.release()
while( counter.get() < n );
然而,轮询变量的值并不是一个好主意,因为 CPU 大部分时间只是旋转,给宇宙增加热量。有更好的方法来做到这一点。
这里有一个关于如何解决这个难题的非常慷慨的提示:想象一下,有 N 个线程正在等待 acquire() 一个没有许可的信号量。如果您在信号量上释放()N个许可,所有线程都将继续。