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

发布于 2024-12-18 11:58:52 字数 202 浏览 5 评论 0原文

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

编辑注意:意识到我说的是“BINARY”信号量类。我已经完成了标准信号量类,并且我知道它是正确的,因此标准信号量类对我不感兴趣。

I can see how a "standard" Semaphore Class can be implemented in Java. However, I cant see how to implement a Binary Semaphore Class in Java. How does such implementation work? When should I call the wake and notify methods to wake and stop the threads that are on the semaphores?
I understand what binary semaphores are, but I have no idea of how to code them.

Edit Note: Realize that I said "BINARY" Semaphore class. The standard Semaphore class I already did and I know its correct so the standard Semaphore Class does not interest me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

灵芸 2024-12-25 11:58:52

我认为您正在谈论互斥锁(或互斥锁)。如果是这样,您可以使用内在锁。 Java 中的这种锁充当互斥体,这意味着最多一个线程可以拥有该锁:

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

其中 lock 是一个模拟对象,仅用于锁定。


编辑:

这是一个为您提供的实现 - 不可重入互斥锁类,它使用值零表示解锁状态,使用值 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 <- LockSupport <- Unsafe)。

希望这有帮助。

I think you're talking about mutex (or mutual exclusion locks). If so, you can use intrinsic locks. This kind of locks in Java act as mutexes, which means that at most one thread may own the lock:

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

Where lock is a mock object, used only for locking.


EDIT:

Here is an implementation for you — non-reentrant mutual exclusion lock class that uses the value zero to represent the unlocked state, and one to represent the locked state.

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

If you need to know where should you call wait() and notify(), have a look at sun.misc.Unsafe#park(). It is used within java.util.concurrent.locks package (AbstractQueuedSynchronizer <- LockSupport <- Unsafe).

Hope this helps.

悲喜皆因你 2024-12-25 11:58:52

这是我为二进制信号量所做的一个简单实现:

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

该实现具有二进制信号量的一个属性,您无法通过对只有一个许可的信号量进行计数来获得该属性 - 多次调用release仍将只留下一个可用资源。 此处提到了此属性。

Here is a simple implementation I did for a binary semaphore:

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

This implementation has one property of binary semaphores that you cannot get with counting semaphores that only have one permit - multiple calls to release will still leave just one resource available. This property is mentioned here.

淡忘如思 2024-12-25 11:58:52

是的,你可以。具有单个许可的信号量是二进制信号量。它们控制对单个资源的访问。它们可以被视为某种互斥锁/锁。

Semaphore binarySemaphore = new Semaphore(1);

Yes, you can. A semaphore with a single permit is a binary semaphore. They control access to a single resource. They can be viewed as some kind of a mutex/lock.

Semaphore binarySemaphore = new Semaphore(1);
护你周全 2024-12-25 11:58:52

这里直接来自 Java 站点

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

这样的锁之一就是众所周知的信号量。信号量可用于
与现在使用 wait 的方式相同,限制对某个块的访问
代码。信号量更加灵活,还可以允许多个
并发线程访问,以及允许您在之前测试锁
获取它。以下示例也仅使用一个信号量
称为二进制信号量。请参阅 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 类等更高级别抽象的全部原因是您不必调用低级别 wait/notify

Here is straight from the Java site

The concurrency utility library, led by Doug Lea in JSR-166, is a
special release of the popular concurrency package into the J2SE 5.0
platform. It provides powerful, high-level thread constructs,
including executors, which are a thread task framework, thread safe
queues, Timers, locks (including atomic ones), and other
synchronization primitives.

One such lock is the well known semaphore. A semaphore can be used in
the same way that wait is used now, to restrict access to a block of
code. Semaphores are more flexible and can also allow a number of
concurrent threads access, as well as allow you to test a lock before
acquiring it. The following example uses just one semaphore, also
known as a binary semaphore. See the java.util.concurrent package for
more information.

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
}

I think, the whole reason of using higher-level abstracts such as Semaphore class is that you don't have to call low level wait/notify.

西瓜 2024-12-25 11:58:52

我在 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);
    }
}

如果您在代码中发现任何错误,请告诉我,但到目前为止它总是工作正常! :)

I have my own implementation of a Binary Semaphore in 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);
    }
}

Tell me if you find any bug in the code please, but so far it always worked fine! :)

请你别敷衍 2024-12-25 11:58:52

我宁愿使用 Lock class

除了命名匹配之外,Java Semaphore 无法实现 BinarySemaphore,并且使用 Object 等待/通知或同步非常原始。

相反,Lock 类通过其锁定/解锁(与信号量的获取/释放)提供了与 Semaphore 几乎相同的锁定语义,但它专门用于解决临界区功能,其中一次只需要一个线程进入。

值得注意的是,由于 tryLock 方法。

I would rather use the Lock class

Besides the naming matching, Java Semaphore is no way to implement a BinarySemaphore, and using Object wait/notify or synchronize is quite raw.

Instead, the Lock class provides almost the same locking semantics as a Semaphore with its lock/unlock (versus acquire/release by Semaphore), but it is specifically targeted to solve critical section functionality, where just one thread is expected to enter at once.

Worth noting Lock also provide try with timeout semantics thanks to tryLock method.

归途 2024-12-25 11:58:52

也许使用 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();
    }
}

Maybe it's a good idea to use AtomicBoolean implement it.
If it's not, please let me know.

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();
    }
}
梦中楼上月下 2024-12-25 11:58:52

您可以查看 Semaphore 类(或者直接使用它?)

You could have a look at the source code for the Java implementation of the Semaphore class (or perhaps use it directly?)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文