Java并发性-使用信号量实现监视器-所有线程都陷入等待状态,我的理解有问题
我试图用Java中的信号量实现监视器,用一些写线程和一些读线程创建一个有界缓冲区 到目前为止,我做了以下工作: 对于我们可能想要锁定的每个类,比如之前,我们会在其中有一个同步代码块,我添加了两个信号量,一个二进制代码,用于在块开始时锁定,在块结束时解锁,以确保在任何时候只能执行一个关键代码段,另一个作为传递notify和notifyAll信号的单元。我还创建了一个整数计数器来跟踪调用wait的线程 然后,在一个同步代码块的开始,我获得了“监视器”的锁,然后在必要时调用我的替换程序以获得wait指令 我有两个线程不断调用put和两个线程不断调用get。在1秒到10秒之间,所有线程都会卡住 不知怎的,他们都被困在等待中,我真的看不出是怎么回事!我花了几天时间仔细考虑这件事。有什么想法吗 有人知道是什么导致所有这些线程在这一点上卡住了吗 谢谢,我认为您的notifyAll实现存在缺陷。要正确执行此操作,您需要一个联锁的比较和交换操作,就像AtomicInteger等人提供的那样。问题在于,在您的循环中:Java并发性-使用信号量实现监视器-所有线程都陷入等待状态,我的理解有问题,java,multithreading,concurrency,semaphore,monitor,Java,Multithreading,Concurrency,Semaphore,Monitor,我试图用Java中的信号量实现监视器,用一些写线程和一些读线程创建一个有界缓冲区 到目前为止,我做了以下工作: 对于我们可能想要锁定的每个类,比如之前,我们会在其中有一个同步代码块,我添加了两个信号量,一个二进制代码,用于在块开始时锁定,在块结束时解锁,以确保在任何时候只能执行一个关键代码段,另一个作为传递notify和notifyAll信号的单元。我还创建了一个整数计数器来跟踪调用wait的线程 然后,在一个同步代码块的开始,我获得了“监视器”的锁,然后在必要时调用我的替换程序以获得wait指
// Equivalent of notifyAll()
for (int i = val(); i>0; i--) {
dec();
notifyCalled.release();
}
两个线程可以相互竞争,并且都将val观察为1,然后两个dec调用都将成功,blocksWaitingCount将为-1。然后,由于blocksWaitingCount不再与等待notifyCalled许可证的线程数匹配,因此notifyAll的未来调用将无法通知所有阻塞线程,因为即使i==0,仍然存在线程阻塞。重复几次迭代,notifyAll最终将停止释放任何线程,所有线程都将被阻止。问题在于您无法控制哪个线程将成功调用notifyCalled.acquire 例如,考虑这种情况: 穿一件等待的衣服 线程B执行两次put并填充缓冲区。只有一个线程在等待,因此它对notifyCalled.release进行了1次调用 线程C执行put,由于缓冲区已满,它进入等待块 3中的notifyCalled.release导致notifyCalled.aquire在线程C而不是线程A中成功 由于缓冲区仍然是满的,线程C和所有其他put操作进入A,重新进入while循环并再次等待,线程B将永远不会收到它正在等待的释放 解决方案 当put操作的等待块中获得的许可证由于缓冲区已满或get操作相反而未被使用时,就会出现问题。为了避免这种情况,可以使用一个标志来释放许可证,以便另一个等待的线程可以尝试使用相反的操作来获取许可证 此外,如Daniel Pryden inc.所述,还应将其移动到监视器信号量锁内,以避免竞争条件 请注意,这使得用于修改blocksWaitingCount的同步块变得不必要,尽管由于它们是无竞争的,所以对性能的影响最小 这是修改后的代码
public class BufferNonSync {
private int[] buffer = new int[] { 0};
private int start = 0;
private int last = 0;
private final int size = 1;
private int numberInBuffer = 0;
// Monitor variables
private Semaphore monitorSemaphore = new Semaphore(1);
private Semaphore notifyCalled = new Semaphore(0);
private int blocksWaitingCount = 0;
public void put(int input, int id) throws InterruptedException {
monitorSemaphore.acquire();
boolean acquired = false;
while (numberInBuffer == size) {
// Equivalent of wait()
if (acquired) {
dec();
notifyCalled.release();
}
inc();
monitorSemaphore.release();
notifyCalled.acquire();
monitorSemaphore.acquire();
acquired = true;
}
// Critical section
buffer[last] = input;
last = (last + 1) % size;
numberInBuffer++;
// Equivalent of notifyAll()
for (int i = val(); i > 0; i--) {
dec();
notifyCalled.release();
}
monitorSemaphore.release();
}
public int get(int id) throws InterruptedException {
monitorSemaphore.acquire();
boolean acquired = false;
while (numberInBuffer == 0) {
// Equivalent of wait()
if (acquired) {
dec();
notifyCalled.release();
}
inc();
monitorSemaphore.release();
notifyCalled.acquire();
monitorSemaphore.acquire();
acquired = true;
}
// Critical section
int temp = buffer[start];
start = (start + 1) % size;
numberInBuffer--;
// Equivalent of notifyAll()
for (int i = val(); i > 0; i--) {
dec();
notifyCalled.release();
}
monitorSemaphore.release();
return temp;
}
private void inc() {
blocksWaitingCount++;
}
private void dec() {
blocksWaitingCount--;
}
private int val() {
return blocksWaitingCount;
}
}
我不知道这是否相关,但是,它让我想到了这一点:blocksWaitingCount在我看来应该是易变的或原子整数:否则就不能保证其他线程会看到更新的值。嗯,这一点很好。但是在您的等待实现中,inc不受monitorSemaphore的保护,因此您仍然有在调用val和dec之间更改blocksWaitingCount的风险,对吗?blocksWaitingCount的原因是因为您试图确保每个notifyCalled.acquire都与一个notifyCalled.release配对,对吗?但是因为wait实现在执行inc时不持有monitorSemaphore;notifyCalled.acquire在相应的acquire发生之前,您可能会让notifyAll实现执行其发布。put线程只需要得到get线程的通知,反之亦然。也许你可以把你的名字分成两个不同的对象