如何在Java中编写信号量,优先考虑以前成功的申请人?

jju*_*uma 11 java concurrency multithreading

我需要在我的Java程序中使用单许可证信号量对象,其中有一个额外的获取方法,如下所示:

boolean tryAcquire(int id)
Run Code Online (Sandbox Code Playgroud)

并且行为如下:如果之前没有遇到过id,那么记住它,然后做任何事情java.util.concurrent.Semaphore.如果ID 被遇到过那次偶遇导致许可证的租约然后在谁可能正在等待许可证的所有其他线程给这个线程优先级.我还想要一个额外的发布方法,如:

void release(int id)
Run Code Online (Sandbox Code Playgroud)

它可以做任何事情java.util.concurrent.Semaphore,还可以"忘记"id.

我真的不知道如何处理这个问题,但这是可能实现的开始,但我担心它无处可去:

public final class SemaphoreWithMemory {

    private final Semaphore semaphore = new Semaphore(1, true);
    private final Set<Integer> favoured = new ConcurrentSkipListSet<Integer>();

    public boolean tryAcquire() {
        return semaphore.tryAcquire();
    }

    public synchronized boolean tryAcquire(int id) {
        if (!favoured.contains(id)) {
            boolean gotIt = tryAcquire();
            if (gotIt) {
                favoured.add(id);
                return true;
            }
            else {
                return false;
            }
        }
        else {
            // what do I do here???
        }
    }

    public void release() {
        semaphore.release();
    }

    public synchronized void release(int id) {
        favoured.remove(id);
        semaphore.release();
    }

}
Run Code Online (Sandbox Code Playgroud)

Enn*_*oji 1

编辑:
做了一些实验。请参阅此答案以获取结果。

原则上,Semaphore 内部有一个线程队列,因此就像 Andrew 所说,如果您将此队列设为优先级队列并从该队列轮询以发出许可,它可能会按照您想要的方式运行。请注意,您不能这样做,tryAcquire因为这样线程就不会排队。从我看来,你必须破解AbstractQueuedSynchronizer类才能执行此操作。

我还可以想到一种概率方法,如下所示:(
我并不是说下面的代码是个好主意!只是在这里集思广益。)

public class SemaphoreWithMemory {

    private final Semaphore semaphore = new Semaphore(1);
    private final Set<Integer> favoured = new ConcurrentSkipListSet<Integer>();
    private final ThreadLocal<Random> rng = //some good rng

    public boolean tryAcquire() {
        for(int i=0; i<8; i++){
            Thread.yield();
            // Tend to waste more time than tryAcquire(int id)
            // would waste.
            if(rng.get().nextDouble() < 0.3){
                return semaphore.tryAcquire();
            }
        }
        return semaphore.tryAcquire();
    }

    public boolean tryAcquire(int id) {
        if (!favoured.contains(id)) {
            boolean gotIt = semaphore.tryAcquire();
            if (gotIt) {
                favoured.add(id);
                return true;
            } else {
                return false;
            }
        } else {
            return tryAquire();
    }
}
Run Code Online (Sandbox Code Playgroud)

或者让“喜欢的”线程挂得更久一点,如下所示:
编辑:事实证明这是一个非常糟糕的主意(无论是公平信号量还是非公平信号量)(有关详细信息,请参阅我的实验。

    public boolean tryAcquire(int id) {
        if (!favoured.contains(id)) {
            boolean gotIt = semaphore.tryAcquire(5,TimeUnit.MILLISECONDS);
            if (gotIt) {
                favoured.add(id);
                return true;
            } else {
                return false;
            }
        } else {
            return tryAquire();
    }
Run Code Online (Sandbox Code Playgroud)

我想这样你就可以对许可证的发放方式产生偏见,但这并不公平。尽管使用这段代码,您可能会在性能方面浪费大量时间......