以下代码我从Semaphore的oracle文档中获取。
我的问题是,如果我正在同步getNextAvailableItem()和markAsUnused方法,这将阻止其他99个线程进入main函数,该函数要么给我共享资源要么接受它。那么信号量的用途是什么呢?无论是99还是1000线程都在等待锁定无关紧要。
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
// Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null;
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
答案 0 :(得分:1)
问题是:在有可用项目之前,您是否希望线程阻塞?
如果是,那么信号量是必要的,因为它基本上是一个计数器,当它已经为零并且一个线程试图获取它时阻塞。
显然你可以尝试在没有信号量的同步方法中自己实现(即使用一些低级结构,如wait
/ notify
),但这只是一个难以发现的bug的邀请
如果不需要阻塞,并且您使用将null返回给调用者的方法很酷,那么您可以跳过信号量。
答案 1 :(得分:1)
要问的问题是,如果调用getItem
时项目不可用,该怎么办?显然,这里的要求是等到一个可用。单独使用synchronized
将不起作用,因为它不允许在解锁状态下等待(并且在锁定状态下等待会阻止任何人释放任何东西)。
计数信号量是实现此目的的最有效方式。当您的available.acquire();
成功返回时(即不会抛出InterruptedException
),已知至少有一个项目可用(因为项目计数与信号量的初始计数匹配) ,并且免费物品的数量与信号量计数器保持同步。
有其他选择,但它们并不是那么好。
您可以在Java中使用互斥锁和条件变量(称为Lock
和Condition
(或者您也可以使用Java的wait
和notify
以及{{ 1}})。 synchronized
将有一个循环:锁定互斥锁,看看项是否可用:如果是,则接受并返回,否则等待条件变量并再试一次。然后getItem
会在释放一个项目后发出条件变量,这会唤醒任何服务员,因此他们知道要检查是否有空。但这是一些代码,可能效率稍低(特别是如果所有服务员被唤醒而不只是一个),而且Java putItem
也没有提供公平的方式排队,而Condition
具有公平性布尔属性,因此服务员按顺序提供。
然后一个更糟糕的替代方案是只有Semaphore
或Lock
块,然后在循环中检查项目是否可用并在可能的情况下接受它,否则(可选)睡眠a位,然后再试一次。但这是低效的,丑陋的,不会扩展到许多线程,并且会让任何有经验的开发人员感到畏缩。