java - finally 子句中的信号量操作

标签 java concurrency semaphore

我正在阅读《Java 并发实践》。

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而不是简单的语句有什么好处?

最佳答案

TL:DR: 调用 Collection#add(E)获得许可后可能会抛出异常。


在这种情况下,信号量的许可代表容量。添加元素时,将获取许可,然后尝试将该元素添加到内部 Set 中。然后检查调用 add 的结果以确保元素确实已添加。如果未添加该元素,则必须释放之前获得的许可证。到目前为止一切都很好。

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

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 block ,因此代码已损坏:

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;
}

关于java - finally 子句中的信号量操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60966503/

相关文章:

c++ - mutex.lock 与 unique_lock

java - join 执行不完整

java - 使用信号量的监控程序在java中无法按预期工作

c - Mach 信号量是否在进程之间的共享内存中工作?

java - gRPC Java客户端: get connected endpoint

使用集合删除方法时不会出现 Java 并发修改异常

javascript - 测试框架意外退出 - Karate 测试

c# - 并发下的 Queue<T> 为不同的类型提供不同的结果

c# - 所有线程一次只能使用一种方法?

java - 使用Restful接口(interface)上传文件