Манипуляция семафорами в пункте finally - PullRequest
2 голосов
/ 01 апреля 2020

Я читаю 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 вместо простых операторов?

1 Ответ

3 голосов
/ 01 апреля 2020

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...