Как реализовать двоичный Семафорный класс в Java?


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

Правка Примечание: поймите, что я сказал "двоичный" Семафорный класс. Стандартный Семафорный класс я уже сделал, и я знаю его правильно, поэтому стандартный Семафорный класс меня не интересует.

7 2

7 ответов:

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

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

Где 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.утиль.параллельный.блокирует пакет (AbstractQueuedSynchronizer

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

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

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

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

Здесь прямо с сайта Java

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

Одним из таких замков является хорошо известный семафор. Один семафор можно использовать в таким же образом, как ожидание используется сейчас, чтобы ограничить доступ к блоку код. Семафоры более гибки и могут также позволить несколько параллельные потоки доступа, а также позволяют протестировать блокировку перед приобретая его. В следующем примере используется только один семафор, также известен как двоичный семафор. Смотрите на яву.утиль.параллельный пакет для подробная информация.
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
}

Я думаю, что вся причина использования абстракций более высокого уровня, таких как семафор класс-это то, что вы не должны называть низким уровнем wait/notify.

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

Semaphore binarySemaphore = new Semaphore(1);

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

У меня есть собственная реализация двоичного семафора в 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);
    }
}

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

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

Помимо сопоставления имен, семафор Java не является способом реализации BinarySemaphore, и использование Object wait/notify или synchronize является довольно сырым.

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

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