【问题标题】:Semaphore manipulation in finally clausefinally 子句中的信号量操作
【发布时间】:2020-04-01 08:25:41
【问题描述】:

我正在阅读 Java Concurrency In Practice。

public class BoundedHashSet<T> {
  private final Set<T> set;
  private final Semaphore sem;

  public BoundedHashSet(int bound) {
    this.set = Collections.synchronizedSet(new HashSet<T>());
    sem = new Semaphore(bound);
  }

  public boolean add(T o) throws InterruptedException {
    sem.acquire();
    boolean wasAdded = false;
    try {
      wasAdded = set.add(o);
      return wasAdded;
    } finally {
      if (!wasAdded)
        sem.release();
    }
  }

  public boolean remove(Object o) {
    boolean wasRemoved = set.remove(o);
    if (wasRemoved)
      sem.release();
    return wasRemoved;
  }
}

我不知道为什么add 方法有try-finally 子句而remove 方法没有。 我认为可以像下面这样使其更简单。

public boolean add(T o) throws InterruptedException {
   sem.acquire();
   boolean wasAdded = set.add(o);
   if (!wasAdded)
       sem.release();
   return wasAdded;
}

使用 try-finally 代替简单语句有什么好处?

【问题讨论】:

    标签: java concurrency semaphore


    【解决方案1】:

    TL:DR:Collection#add(E) 的调用可能会在获得许可后引发异常。


    在这种情况下Semaphore 的许可表示容量。当添加一个元素时,会获得一个许可然后尝试将该元素添加到内部Set。然后检查调用add 的结果以确保实际添加了元素。如果未添加该元素,则必须释放先前获得的许可。到目前为止一切顺利。

    问题是Collection#add(E) 可以抛出异常。在这种情况下,仍必须释放所获得的许可证。 try-finally 块保证如果由于不允许重复或抛出异常而未添加元素,则释放先前获得的许可。

    public boolean add(T o) throws InterruptedException {
      sem.acquire(); // permit acquired before attempting to add element
      boolean wasAdded = false;
      try {
        wasAdded = set.add(o);
        return wasAdded;
      } finally {
        if (!wasAdded)
          sem.release(); // invoked if "set.add(o)" returns false **or** throws exception
      }
    }
    

    由于您的版本没有 try-finally 块,因此代码已损坏:

    public boolean add(T o) throws InterruptedException {
       sem.acquire(); // permit acquired before attempting to add element
       boolean wasAdded = set.add(o); // if exception is thrown permit is never released
       if (!wasAdded)
           sem.release(); // only invoked if "set.add(o)" returns false
       return wasAdded;
    }
    

    删除一个元素没有同样的问题。在尝试移除元素之前没有释放许可证,因此如果元素实际上没有被移除,则无需重新获取许可证。即使Collection#remove(Object) 抛出异常,情况仍然如此。

    public boolean remove(Object o) {
      boolean wasRemoved = set.remove(o);
      if (wasRemoved)
        sem.release(); // won't be invoked if "set.remove(o)" returns false or throws exception
      return wasRemoved;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-12-04
      • 1970-01-01
      • 2012-02-25
      • 2015-10-28
      • 1970-01-01
      • 2011-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多