首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用Java编写一个信号量,优先考虑先前的成功申请者?

如何用Java编写一个信号量,优先考虑先前的成功申请者?
EN

Stack Overflow用户
提问于 2011-03-05 19:42:55
回答 5查看 6K关注 0票数 11

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

代码语言:javascript
复制
boolean tryAcquire(int id)

它的行为如下:如果之前没有遇到id,那么记住它,然后做java.util.concurrent.Semaphore做的任何事情。如果在此之前遇到id,并且该遭遇导致许可证的租赁,则将此线程优先级赋予可能等待许可的所有其他线程。我还需要一个额外的发布方法,比如:

代码语言:javascript
复制
void release(int id)

它可以做java.util.concurrent.Semaphore做的任何事情,还可以“忘记”id。

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

代码语言:javascript
复制
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();
    }

}
EN

回答 5

Stack Overflow用户

发布于 2011-03-05 20:40:27

编辑:

做了一些实验。有关结果,请参见这个答案

原则上,信号量在内部有一个线程队列,所以就像Andrew说的,如果您将这个队列作为优先级队列并从该队列轮询以发出许可,那么它的行为可能会以您想要的方式进行。请注意,您不能用tryAcquire完成这一任务,因为这样线程就不会排队。据我所见,要做到这一点,您必须黑掉AbstractQueuedSynchronizer类。

我也能想到一种概率方法,就像这样:

(我不是说下面的代码是个好主意!在这里集思广益。)

代码语言:javascript
复制
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();
    }
}

或者让“受欢迎的”线程像这样逗留更久一点:

编辑:发现这是一个非常糟糕的主意(包括公平信号量和非公平信号量)(详见我的实验 )。

代码语言:javascript
复制
    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();
    }

我想这样你可能会对发放许可证的方式产生偏见,而这是不公平的。虽然有了这段代码,你可能会浪费很多时间--从性能上讲.

票数 1
EN

Stack Overflow用户

发布于 2011-09-06 14:52:38

对于阻塞获取模式,以下内容如何:

代码语言:javascript
复制
public class SemWithPreferred {
    int max;
    int avail;
    int preferredThreads;

    public SemWithPreferred(int max, int avail) {
        this.max = max;
        this.avail = avail;
    }

    synchronized public void get(int id) throws InterruptedException {
        boolean thisThreadIsPreferred = idHasBeenServedSuccessfullyBefore(id);
        if (thisThreadIsPreferred) {
            preferredThreads++;
        }
        while (! (avail > 0 && (preferredThreads == 0 || thisThreadIsPreferred))) {
            wait();
        }
        System.out.println(String.format("granted, id = %d, preferredThreads = %d", id, preferredThreads));
        avail -= 1;
        if (thisThreadIsPreferred) {
            preferredThreads--;
            notifyAll(); // removal of preferred thread could affect other threads' wait predicate
        }
    }

    synchronized public void put() {
        if (avail < max) {
            avail += 1;
            notifyAll();
        }
    }

    boolean idHasBeenServedSuccessfullyBefore(int id) {
        // stubbed out, this just treats any id that is a 
        // multiple of 5 as having been served successfully before 
        return id % 5 == 0;
    }
}
票数 1
EN

Stack Overflow用户

发布于 2011-03-05 21:13:39

假设您希望线程等待,我破解了一个不完美的解决方案,但应该这样做。

其想法是有两个信号量和一个“最受欢迎的是等待”的旗帜。

每个试图获取SemaphoreWithMemory的线程都首先尝试获取"favouredSemaphore“。一个“受欢迎的”线程保持信号量,而一个不受欢迎的线程会立即释放它。因此,一旦他获得了这个信号量,偏爱的线程就会阻塞所有其他传入线程。

然后,必须获得第二个"normalSemaphore“才能完成。但是,非偏好线程然后再次检查是否存在使用易失性变量等待的偏爱线程)。如果没有人在等待,那么他只是在继续;如果一个人在等待,他释放normalSemaphore并递归地再次调用获取。

我不太确定是否有任何种族条件潜伏。如果您想要确定,您也许应该重构您的代码,将“工作项”交给优先级队列,在该队列中,另一个线程接受具有最高优先级的工作项并执行该代码。

代码语言:javascript
复制
public final class SemaphoreWithMemory {

private volatile boolean favouredAquired = false;
private final Semaphore favouredSemaphore = new Semaphore(1, true);
private final Semaphore normalSemaphore = new Semaphore(1, true);
private final Set<Integer> favoured = new ConcurrentSkipListSet<Integer>();

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

public void acquire(int id) throws InterruptedException {
    boolean idIsFavoured = favoured.contains(id);
    favouredSemaphore.acquire();
    if (!idIsFavoured) {
        favouredSemaphore.release();
    } else {
        favouredAquired = true;
    }
    normalSemaphore.acquire();

    // check again that there is no favoured thread waiting
    if (!idIsFavoured) {
        if (favouredAquired) {
            normalSemaphore.release();
            acquire(); // starving probability!
        } else {
            favoured.add(id);
        }
    }

}

public void release() {
    normalSemaphore.release();
    if (favouredAquired) {
        favouredAquired = false;
        favouredSemaphore.release();
    }
}

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

}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5206318

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档