如何在Java中实现二进制信号量类?

如何在Java中实现二进制信号量类?,java,multithreading,concurrency,semaphore,concurrent-programming,Java,Multithreading,Concurrency,Semaphore,Concurrent Programming,我可以看到如何在Java中实现“标准”信号量类。但是,我看不到如何在Java中实现二进制信号量类。这种实施是如何运作的?我应该在什么时候调用wake并通知方法来唤醒和停止信号量上的线程? 我理解什么是二进制信号量,但我不知道如何对它们进行编码 编辑说明:认识到我说的“二进制”信号量类。我已经使用了标准信号量类,我知道它是正确的,所以我对标准信号量类不感兴趣。您可以查看该类的Java实现的源代码(或者直接使用它?这里是直接从 由Doug Lea在JSR-166中领导的并发实用程序库是一个 J2SE

我可以看到如何在Java中实现“标准”信号量类。但是,我看不到如何在Java中实现二进制信号量类。这种实施是如何运作的?我应该在什么时候调用wake并通知方法来唤醒和停止信号量上的线程? 我理解什么是二进制信号量,但我不知道如何对它们进行编码


编辑说明:认识到我说的“二进制”信号量类。我已经使用了标准信号量类,我知道它是正确的,所以我对标准信号量类不感兴趣。

您可以查看该类的Java实现的源代码(或者直接使用它?

这里是直接从

由Doug Lea在JSR-166中领导的并发实用程序库是一个 J2SE 5.0中流行并发程序包的特别版本 站台。它提供了强大的高级线程构造, 包括执行器,这是一个线程任务框架,线程安全 队列、计时器、锁(包括原子锁)和其他 同步原语

一个这样的锁是众所周知的信号量。信号量可用于 与现在使用的wait相同,它限制对一个数据块的访问 代码。信号量更灵活,还可以允许许多 并发线程访问,以及允许您在访问之前测试锁 获得它。下面的示例也仅使用一个信号量 被称为二进制信号量。有关详细信息,请参阅java.util.concurrent包 更多信息


我认为,使用高级抽象(如信号量类)的全部原因是您不必调用低级
wait
/
notify
是的,您可以。具有单个许可证的信号量是二进制信号量。它们控制对单个资源的访问。它们可以被视为某种互斥锁/锁

Semaphore binarySemaphore = new Semaphore(1);

我想你说的是互斥锁(互斥锁)。如果是这样,您可以使用内部锁。Java中的这种锁充当互斥锁,这意味着最多一个线程可以拥有锁:

synchronized (lock) { 
    // Access or modify shared state guarded by lock 
}
其中lock是一个模拟对象,仅用于锁定


编辑:

下面是一个为您提供的实现—不可重入互斥锁类,它使用值0表示解锁状态,使用值1表示锁定状态

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)中使用。下面是我为二进制信号量所做的一个简单实现:

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中的二进制信号量的实现

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);
    }
}
如果您在代码中发现任何错误,请告诉我,但到目前为止,它一直运行良好!:)

我宁愿使用该类

除了命名匹配之外,Java信号量无法实现二进制信号量,使用对象等待/通知或同步是非常原始的

相反,Lock类提供的锁定语义与信号量的Lock/unlock(与信号量的acquire/release相比)几乎相同,但它专门用于解决关键部分功能,其中一次只需要一个线程


值得注意的是,由于该方法,Lock还提供了try-with-timeout语义。

也许使用AtomicBoolean实现它是个好主意。 如果不是,请告诉我

import java.util.concurrent.atomic.AtomicBoolean;

public class BinarySemaphore {
    
    private final AtomicBoolean permit;
    
    public BinarySemaphore() {
        this(true);
    }
    
    /**
     * Creates a binary semaphore with a specified initial state
     */
    public BinarySemaphore(boolean permit) {
        this.permit = new AtomicBoolean(permit);
    }

    public void acquire() {
        boolean prev;
        do {
            prev = tryAcquire();
        } while (!prev);
    }

    public boolean tryAcquire() {
        return permit.compareAndSet(true, false);
    }

    /**
     * In any case, the permit was released
     */
    public void release() {
        permit.set(true);
    }

    public boolean available(){
        return permit.get();
    }
}

你试过什么吗;利用现有信号量类的源代码,说明如果只有1的许可证,它将如何运行。您是否阅读过(例如在wikipedia上)信号量的定义?你能解释一下为什么给出的答案(特别是维卡斯的答案或亚历山大更广泛的答案)是不够的吗?否则,您可以尝试解释用例(为什么需要二进制信号量/互斥量?)它没有帮助,我需要一个二进制信号量类,而不是标准类。如果它只有一个许可证,那么它就是一个二进制信号量。你的评论就像说“这辆车不合适:它可以达到180公里/小时,我需要达到90公里/小时。你可以扩展标准信号量并创建自己的二进制信号量类,如下所示。公共类BinarySemaphore扩展信号量{BinarySemaphore(){super(1);}”@JBNizet将计数信号量中的许可数设置为1不会自动生成二进制信号量,因为通过调用
release()
多次,一个线程仍然可以将可用许可证的数量增加到大于1的值。我需要的是二进制信号量,而不是那个信号量。你说的是我应该在二进制信号量类中放入的内容。我已经知道我应该在某个时候这样做,但我不知道应该在哪里调用notify和wait调用。你不知道不需要深入研究这样一个细节级别(但如果您愿意,请查看
sun.misc.Unsafe
类,正如我在文章中提到的)。您可以使用派生的方便实现来实现您的目标。这并不安全-只有release()是同步的,因此if acquire在if(countingSemaphore.availablePermits)之后并发运行以发布() != 1),在调用release之前,可能会出现意外的行为。此外,使acquire synchronized也不起作用,因为锁被锁定会阻止您释放二进制信号量,从而停止应用程序。@Samheath如果非同步方法acquire像您所说的那样启动,我正在考虑您所说的在!=1检查之后,将没有可用的插槽,内部acquire调用将停止,线程将等待,上下文切换,另一个释放
import java.util.concurrent.atomic.AtomicBoolean;

public class BinarySemaphore {
    
    private final AtomicBoolean permit;
    
    public BinarySemaphore() {
        this(true);
    }
    
    /**
     * Creates a binary semaphore with a specified initial state
     */
    public BinarySemaphore(boolean permit) {
        this.permit = new AtomicBoolean(permit);
    }

    public void acquire() {
        boolean prev;
        do {
            prev = tryAcquire();
        } while (!prev);
    }

    public boolean tryAcquire() {
        return permit.compareAndSet(true, false);
    }

    /**
     * In any case, the permit was released
     */
    public void release() {
        permit.set(true);
    }

    public boolean available(){
        return permit.get();
    }
}