在我的Java程序中,我需要一个单许可证信号量对象,其中有一个额外的获取方法,如下所示:
boolean tryAcquire(int id)它的行为如下:如果之前没有遇到id,那么记住它,然后做java.util.concurrent.Semaphore做的任何事情。如果在此之前遇到id,并且该遭遇导致许可证的租赁,则将此线程优先级赋予可能等待许可的所有其他线程。我还需要一个额外的发布方法,比如:
void release(int id)它可以做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();
}
}发布于 2011-03-05 20:40:27
编辑:
做了一些实验。有关结果,请参见这个答案。
原则上,信号量在内部有一个线程队列,所以就像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();
}
}或者让“受欢迎的”线程像这样逗留更久一点:
编辑:发现这是一个非常糟糕的主意(包括公平信号量和非公平信号量)(详见我的实验 )。
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();
}我想这样你可能会对发放许可证的方式产生偏见,而这是不公平的。虽然有了这段代码,你可能会浪费很多时间--从性能上讲.
发布于 2011-09-06 14:52:38
对于阻塞获取模式,以下内容如何:
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;
}
}发布于 2011-03-05 21:13:39
假设您希望线程等待,我破解了一个不完美的解决方案,但应该这样做。
其想法是有两个信号量和一个“最受欢迎的是等待”的旗帜。
每个试图获取SemaphoreWithMemory的线程都首先尝试获取"favouredSemaphore“。一个“受欢迎的”线程保持信号量,而一个不受欢迎的线程会立即释放它。因此,一旦他获得了这个信号量,偏爱的线程就会阻塞所有其他传入线程。
然后,必须获得第二个"normalSemaphore“才能完成。但是,非偏好线程然后再次检查是否存在使用易失性变量等待的偏爱线程)。如果没有人在等待,那么他只是在继续;如果一个人在等待,他释放normalSemaphore并递归地再次调用获取。
我不太确定是否有任何种族条件潜伏。如果您想要确定,您也许应该重构您的代码,将“工作项”交给优先级队列,在该队列中,另一个线程接受具有最高优先级的工作项并执行该代码。
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();
}
}https://stackoverflow.com/questions/5206318
复制相似问题