Как реализовать двоичный класс семафоров в Java? - PullRequest
2 голосов
/ 27 ноября 2011

Я вижу, как "стандартный" класс семафоров может быть реализован в Java. Тем не менее, я не вижу, как реализовать двоичный класс семафоров в Java. Как работает такая реализация? Когда мне следует вызывать методы wake и notify, чтобы разбудить и остановить потоки, которые находятся на семафорах? Я понимаю, что такое двоичные семафоры, но понятия не имею, как их кодировать.

Редактировать Примечание. Поймите, что я сказал "БИНАРНЫЙ" класс семафоров. Стандартный класс семафоров, который я уже сделал, и я знаю, что он правильный, поэтому стандартный класс семафоров меня не интересует.

Ответы [ 7 ]

4 голосов
/ 28 апреля 2012

Вот простая реализация, которую я сделал для двоичного семафора:

public class BinarySemaphore {

    private final Semaphore countingSemaphore;

    public BinarySemaphore(boolean available) {
        if (available) {
            countingSemaphore = new Semaphore(1, true);
        } else {
            countingSemaphore = new Semaphore(0, true);
        }
    }

    public void acquire() throws InterruptedException {
        countingSemaphore.acquire();
    }

    public synchronized void release() {
        if (countingSemaphore.availablePermits() != 1) {
            countingSemaphore.release();
        }
    }
}

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

3 голосов
/ 27 ноября 2011

Я думаю, вы говорите о мьютексе (или взаимных исключающих блокировках).Если это так, вы можете использовать встроенные замки.Этот тип блокировок в Java действует как мьютексы, что означает, что блокировка может принадлежать не одному потоку:

synchronized (lock) { 
    // Access or modify shared state guarded by lock 
}

Где блокировка - фиктивный объект, используемый только для блокировки.


РЕДАКТИРОВАТЬ:

Вот реализация для вас - класс блокировки взаимного исключения не реентерабельный, который использует нулевое значение для представления разблокированного состояния и один для представления заблокированного состояния.

class Mutex implements Lock, java.io.Serializable {

    // Our internal helper class
    private static class Sync extends AbstractQueuedSynchronizer {
      // Report whether in locked state
      protected boolean isHeldExclusively() {
        return getState() == 1;
      }

      // Acquire the lock if state is zero
      public boolean tryAcquire(int acquires) {
        assert acquires == 1; // Otherwise unused
        if (compareAndSetState(0, 1)) {
          setExclusiveOwnerThread(Thread.currentThread());
          return true;
        }
        return false;
      }

      // Release the lock by setting state to zero
      protected boolean tryRelease(int releases) {
        assert releases == 1; // Otherwise unused
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
      }

      // Provide a Condition
      Condition newCondition() { return new ConditionObject(); }

      // Deserialize properly
      private void readObject(ObjectInputStream s)
          throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
      }
    }

    // The sync object does all the hard work. We just forward to it.
    private final Sync sync = new Sync();

    public void lock()                { sync.acquire(1); }
    public boolean tryLock()          { return sync.tryAcquire(1); }
    public void unlock()              { sync.release(1); }
    public Condition newCondition()   { return sync.newCondition(); }
    public boolean isLocked()         { return sync.isHeldExclusively(); }
    public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
    public void lockInterruptibly() throws InterruptedException {
      sync.acquireInterruptibly(1);
    }
    public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
      return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
  }

Если вам нужно знать, куда следует звонить wait() и notify(), взгляните на sun.misc.Unsafe#park().Он используется в пакете java.util.concurrent.locks (AbstractQueuedSynchronizer <- LockSupport <- Небезопасно). </p>

Надеюсь, это поможет.

2 голосов
/ 27 ноября 2011

Вот прямо с сайта Java

Библиотека утилит параллелизма, возглавляемая Дугом Ли в JSR-166, представляет собой специальный выпуск популярного пакета параллелизма вПлатформа J2SE 5.0.Он предоставляет мощные высокоуровневые конструкции потоков, в том числе исполнители, которые представляют собой среду задач потоков, очереди с безопасным потоком, таймеры, блокировки (включая атомарные) и другие примитивы синхронизации.

Одна из таких блокировок - это хорошоизвестный семафор.Семафор можно использовать так же, как теперь используется ожидание, чтобы ограничить доступ к блоку кода.Семафоры более гибкие и могут также разрешать доступ к нескольким параллельным потокам, а также позволяют тестировать блокировку перед ее получением.В следующем примере используется только один семафор, также известный как двоичный семафор.См. Пакет java.util.concurrent для получения дополнительной информации.

final  private Semaphore s= new Semaphore(1, true);

    s.acquireUninterruptibly(); //for non-blocking version use s.acquire()

try {     
   balance=balance+10; //protected value
} finally {
  s.release(); //return semaphore token
}

Я думаю, что вся причина использования высокоуровневых тезисов, таких как класс Semaphore, заключается в том, что вам не нужно вызывать lowуровень wait / notify.

1 голос
/ 27 ноября 2011

Да, вы можете.Семафор с одним разрешением - это двоичный семафор.Они контролируют доступ к одному ресурсу.Их можно рассматривать как своего рода мьютекс / блокировку.

Semaphore binarySemaphore = new Semaphore(1);
0 голосов
/ 11 мая 2016

Я бы предпочел использовать класс Lock

Помимо сопоставления имен, Java-семафор не является способом реализации BinarySemaphore, а использование ожиданий / уведомлений или синхронизации объектов является довольно необработанным.

Вместо этого класс Lock обеспечивает почти ту же семантику блокировки, что и семафор, с его блокировкой / разблокировкой (в отличие от захвата / освобождения семафором), но он специально предназначен для решения функций критической секции, где ожидается только один потокдля ввода сразу.

Стоит отметить, что Lock также обеспечивает попытку с семантикой тайм-аута благодаря tryLock .

0 голосов
/ 18 августа 2015

У меня есть собственная реализация двоичного семафора в Java.

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * A binary semaphore extending from the Java implementation {@link Semaphore}.
 * <p>
 * This semaphore acts similar to a mutex where only one permit is acquirable. Attempts to acquire or release more than one permit
 * are forbidden.
 * <p>
 * Has in {@link Semaphore}, there is no requirement that a thread that releases a permit must have acquired that permit. However,
 * no matter how many times a permit is released, only one permit can be acquired at a time. It is advised that the program flow
 * is such that the thread making the acquiring is the same thread making the release, otherwise you may end up having threads
 * constantly releasing this semaphore, thus rendering it ineffective.
 * 
 * @author Pedro Domingues
 */
public final class BinarySemaphore extends Semaphore {

    private static final long serialVersionUID = -927596707339500451L;

    private final Object lock = new Object();

    /**
     * Creates a {@code Semaphore} with the given number of permits between 0 and 1, and the given fairness setting.
     *
     * @param startReleased
     *            <code>true</code> if this semaphore starts with 1 permit or <code>false</code> to start with 0 permits.
     * @param fairMode
     *            {@code true} if this semaphore will guarantee first-in first-out granting of permits under contention, else
     *            {@code false}
     */
    public BinarySemaphore(boolean startReleased, boolean fairMode) {
        super((startReleased ? 1 : 0), fairMode);
    }

    @Override
    public void acquire(int permits) throws InterruptedException {
        if (permits > 1)
            throw new UnsupportedOperationException("Cannot acquire more than one permit!");
        else
            super.acquire(permits);
    }

    @Override
    public void acquireUninterruptibly(int permits) {
        if (permits > 1)
            throw new UnsupportedOperationException("Cannot acquire more than one permit!");
        else
            super.acquireUninterruptibly(permits);
    }

    @Override
    public void release() {
        synchronized (lock) {
            if (this.availablePermits() == 0)
                super.release();
        }
    }

    @Override
    public void release(int permits) {
        if (permits > 1)
            throw new UnsupportedOperationException("Cannot release more than one permit!");
        else
            this.release();
    }

    @Override
    public boolean tryAcquire(int permits) {
        if (permits > 1)
            throw new UnsupportedOperationException("Cannot acquire more than one permit!");
        else
            return super.tryAcquire(permits);
    }

    @Override
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {
        if (permits > 1)
            throw new UnsupportedOperationException("Cannot release more than one permit!");
        else
            return super.tryAcquire(permits, timeout, unit);
    }
}

Скажите, пожалуйста, если вы обнаружите какую-либо ошибку в коде, но пока она всегда работала нормально!:)

0 голосов
/ 27 ноября 2011

Вы могли бы взглянуть на исходный код Java-реализации класса Семафор (или, возможно, использовать его напрямую?)

...