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

时间:2014-09-13 17:07:30

标签: java multithreading

以下代码我从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;
    }
   }

2 个答案:

答案 0 :(得分:1)

问题是:在有可用项目之前,您是否希望线程阻塞?

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

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

如果不需要阻塞,并且您使用将null返回给调用者的方法很酷,那么您可以跳过信号量。

答案 1 :(得分:1)

要问的问题是,如果调用getItem时项目不可用,该怎么办?显然,这里的要求是等到一个可用。单独使用synchronized将不起作用,因为它不允许在解锁状态下等待(并且在锁定状态下等待会阻止任何人释放任何东西)。

计数信号量是实现此目的的最有效方式。当您的available.acquire();成功返回时(即不会抛出InterruptedException),已知至少有一个项目可用(因为项目计数与信号量的初始计数匹配) ,并且免费物品的数量与信号量计数器保持同步。

有其他选择,但它们并不是那么好。

您可以在Java中使用互斥锁和条件变量(称为LockCondition(或者您也可以使用Java的waitnotify以及{{ 1}})。 synchronized将有一个循环:锁定互斥锁,看看项是否可用:如果是,则接受并返回,否则等待条件变量并再试一次。然后getItem会在释放一个项目后发出条件变量,这会唤醒任何服务员,因此他们知道要检查是否有空。但这是一些代码,可能效率稍低(特别是如果所有服务员被唤醒而不只是一个),而且Java putItem也没有提供公平的方式排队,而Condition具有公平性布尔属性,因此服务员按顺序提供。

然后一个更糟糕的替代方案是只有SemaphoreLock块,然后在循环中检查项目是否可用并在可能的情况下接受它,否则(可选)睡眠a位,然后再试一次。但这是低效的,丑陋的,不会扩展到许多线程,并且会让任何有经验的开发人员感到畏缩。