Java-параллелизм: легкий неблокирующий семафор? - PullRequest
3 голосов
/ 22 марта 2011

У меня есть ситуация, когда у меня есть обратный вызов, который я хочу выполнить один раз.В качестве аргумента, скажем, это выглядит так:

final X once = new X(1);
Runnable r = new Runnable() {
    @Override public void run() {
        if (once.use())
           doSomething();
    }
}

, где X - некоторый параллельный объект со следующим поведением:

  • конструктор: X (int N) - выделяет N разрешений на использование

  • boolean use(): если существует хотя бы одно разрешение на использование, используйте одно из них и верните true.В противном случае верните false.Эта операция является атомарной по отношению к нескольким потокам.

Я знаю, что могу использовать java.util.concurrent.Semaphore , но мне не нуженблокирование / ожидание этого, и я хочу, чтобы это было одноразовое использование.

AtomicInteger не выглядит достаточно, если я не делаю что-то вроде

class NTimeUse {
   final private AtomicInteger count;
   public NTimeUse(int N) { this.count = new AtomicInteger(N); }
   public boolean use() {
       while (true)
       {
          int n = this.count.get();
          if (n == 0)
             return false;
          if (this.count.compareAndSet(n, n-1))
             return true;
       }
   }

и я чувствую тошнотуо цикле while.

CountDownLatch не будет работать, потому что метод countDown () не имеет возвращаемого значения и не может быть выполнен атомарно w / r / t getCount ().

Должен ли я просто использовать семафор или есть более подходящий класс?

Ответы [ 3 ]

4 голосов
/ 22 марта 2011

В случае одного разрешения вы можете использовать AtomicBoolean:

final AtomicBoolean once = new AtomicBoolean(true);
Runnable r = new Runnable() {
    @Override public void run() {
        if (once.getAndSet(false))
           doSomething();
    }
}

Если вам нужно много разрешений, используйте ваше решение с compareAndSet(). Не беспокойтесь о петле, getAndIncrement() работает так же под крышкой.

1 голос
/ 22 марта 2011

да. AtomicInteger не блокирует. Вы можете использовать getAndDecrement ().

Вы можете использовать что-то вроде

if(counter.getAndDecrement() > 0) {
   // something
} else {
   counter.set(0);
}

Это сработает, если вы не назовете это два миллиарда раз между декрементом и сетом. т. е. вам нужно будет остановить два миллиарда потоков между этими двумя операторами.

Снова вы можете использовать AtomicLong для дополнительной паранойи.

0 голосов
/ 13 июля 2016
// This implements an unfair locking scheme:
while ( mayContinue() ) {
    // acquire the permit and check if it was legally obtained
    if ( counter.decrementAndGet() > 0 )
        return true;
    // return the illegally acquired permit
    counter.incrementAndGet();
}
return false;

Установка счетчика на ноль, если вы обнаружите, что разрешение было незаконно получено, создает условие гонки, когда другой поток освобождает разрешение. Это работает только в ситуациях, когда максимум 2 или 3 потока. Если у вас есть больше, нужно добавить какой-нибудь другой механизм отката или фиксации.

...