Java 如果我们正在同步函数,为什么需要信号量

Java 如果我们正在同步函数,为什么需要信号量,java,multithreading,Java,Multithreading,下面是我从oracle文档中获取的关于信号量的代码 我的问题是,如果我正在同步getNextAvailableItem()还有Markasused方法,它会阻止其他99个线程进入主函数,要么给我共享资源,要么接受它。那么信号量的用途是什么呢?不管是99个线程还是1000个线程在等待锁 class Pool { private static final int MAX_AVAILABLE = 100; private final Semaphore available = new

下面是我从oracle文档中获取的关于信号量的代码

我的问题是,如果我正在同步getNextAvailableItem()还有Markasused方法,它会阻止其他99个线程进入主函数,要么给我共享资源,要么接受它。那么信号量的用途是什么呢?不管是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;
    }
   }
类池{
专用静态最终int MAX_可用=100;
专用最终可用信号量=新信号量(最大可用信号量,true);
公共对象getItem()引发InterruptedException{
可用。获取();
返回getNextAvailableItem();
}
公共项目(对象x){
如果(使用标记(x))
可用。释放();
}
//不是特别有效的数据结构;只是为了演示
受保护对象[]项=…管理的任何类型的项
受保护的布尔值[]已使用=新的布尔值[MAX_AVAILABLE];
受保护的同步对象getNextAvailableItem(){
对于(int i=0;i
问题是:是否要阻止线程,直到有可用的项目

如果是,那么信号量是必要的,因为它基本上是一个计数器,当它已经为零并且线程试图获取它时,它会阻塞

显然,您可以尝试在没有信号量的同步方法中自己实现这一点(即使用一些低级构造,如
wait
/
notify
),但这只是对难以发现的bug的邀请


如果不需要阻塞,并且您对向调用者返回null的方法很满意,那么您可以跳过信号量。

要问的问题是,如果调用
getItem
时item不可用怎么办。很明显,这里的要求是等待一个可用的。仅使用
synchronized
将不起作用,因为它不允许在解锁状态下等待(而在锁定状态下等待将阻止任何人释放任何东西)

计数信号量是实现这一点最有效的方法。当您的
可用时。获取()
成功返回(即不抛出
InterruptedException
),已知至少有一个项可用(因为项计数与信号量的初始计数匹配,并且空闲项的计数与信号量计数器保持同步)

有其他选择,但没有那么好

您可以使用互斥锁和条件变量,在Java中调用and(或者您也可以使用Java的
wait
notify
,以及
synchronized
)。
getItem
将有一个循环:锁定互斥锁,查看该项是否可用:如果可用,则获取该项并返回,否则等待条件变量并重试。然后,
putItem
将在发布一个项目后向条件变量发送信号,这将唤醒所有服务员,以便他们知道如何检查可用性。但是这是一个代码多一点,效率可能会低一点(特别是当所有服务员都被叫醒而不是一个服务员时),而且Java
条件
不提供公平排队的方法,而
信号量
具有公平布尔属性,因此服务员是按顺序服务的


然后一个更糟糕的选择是只使用一个
同步
块,然后在循环中检查项目是否可用,如果可能的话,将其取下,否则(可选)休眠一点,然后重试。但这是低效的,丑陋的,不能扩展到许多线程,并且会使任何有经验的开发人员畏缩。

Hi hyde..感谢您的回复。我在另一个教程中看到..与信号量同步使用,因为我是java初学者,我想了解它在这里的意义是什么,因为我们将类锁定在对象的互斥锁上,这不会让任何线程得到它。因此,我觉得这里没有必要计算信号量,还有一件事,如果你能给我推荐一些好书,详细解释这些小事情,我将非常高兴非常感谢你,我刚刚添加了新的第1段。总而言之,问题在于等待项目可用,而不阻止其他线程释放项目。