Java 如何编写简单的公平信号量?

Java 如何编写简单的公平信号量?,java,concurrency,semaphore,Java,Concurrency,Semaphore,我发现了信号量(我的自定义信号量)的简单实现,据我所知,这是“不公平”的,因为进入安全块始终只能进入第一个线程(我不确定)。 如何编写公平信号量(模拟并发新信号量(1,true);) 公共类SimpleMaphoreSample2{ CustomSemaphore cSem=新的CustomSemaphore(1); 公共静态void main(字符串[]args){ SimpleSemaphoreSample2 main=新的SimpleSemaphoreSample2(); 信号量sem=新

我发现了信号量(我的自定义信号量)的简单实现,据我所知,这是“不公平”的,因为进入安全块始终只能进入第一个线程(我不确定)。 如何编写公平信号量(模拟并发
新信号量(1,true);

公共类SimpleMaphoreSample2{
CustomSemaphore cSem=新的CustomSemaphore(1);
公共静态void main(字符串[]args){
SimpleSemaphoreSample2 main=新的SimpleSemaphoreSample2();
信号量sem=新信号量(1,true);
Thread thrdA=新线程(main.new SyncOutput(sem,“Thread1”),“Thread1”);
Thread thrdB=新线程(main.new SyncOutput(sem,“Thread2”),“Thread2”);
thrdA.start();
thrdB.start();
试一试{
thrdB.join();
}捕捉(中断异常e){
e、 printStackTrace();
}
系统输出打印项次(“结束”);
}
类SyncOutput实现可运行{
专用信号量;
私有字符串msg;
公共同步输出(信号量s,字符串m){
sem=s;
msg=m;
}
@凌驾
公开募捐{
while(true){
试一试{
//sem.acquire();
cSem.acquire();
系统输出打印项次(“之前”);
睡眠(500);
System.out.println(msg);
睡眠(500);
System.out.println(“之后”);
睡眠(500);
}捕获(异常exc){
exc.printStackTrace();
}
//sem.release();
cSem.release();
}
}
}
公共类自定义信号量{
专用int计数器;
公共自定义信号量(){
这(0);
}
公共自定义信号量(int i){
if(i<0)
抛出新的IllegalArgumentException(i+“<0”);
计数器=i;
}
公共同步无效释放(){
如果(计数器==0){
this.notify();
}
计数器++;
}
public synchronized void acquire()引发InterruptedException{
while(计数器==0){
这个。等等();
}
计数器--;
}
}
}
在这里输入代码
根据它的说法

内在锁定不提供确定性的公平性保证

这里的内在锁定使用的是
同步
。因此,如果不将
synchronized
替换为
Lock Lock=new ReentrantLock(true),就无法使这个信号量示例公平

其中,
true
as构造函数参数告诉ReentrantLock是公平的

根据@trutheality的注释进行编辑


如果您确实希望它在不使用ReentrantLock的情况下是正确的,那么您可以实现从中继承同步原语的信号量。这将被证明是相当复杂的,如果您能正确地使用ReentrantLock编写它,我建议您这样做。注意:ReentrantLock将其同步委托给AQS

您的信号量不公平,因为线程可能永远等待。考虑一个互斥体(二进制信号量),用于通过3个线程写入一个值。T1采集、T2等待和T3等待。现在,在释放过程中,您通知T2和T3之间的一个接收信号量(假设T2)。现在回来等着。当T2通知时,T1接收。它可以发生尽可能多的次数,T3将永远不会有信号灯


一个变化是在信号量内使用简单的FIFO。当线程必须等待时,您可以将其id添加到队列中。现在,当您通知时,您将通知所有线程。唯一正在进行的线程是位于队列头部的线程。

我有一个可重入信号量的示例,但它仅为2个伪装者设计。如果要将代码扩展2个以上,必须实现一个简单的列表并进行一些更改,包括在
aquire()
方法中测试
wait()

package nmscd.utils;

/**
 * A simple, non-reentrant, one permit, FAIR semaphore
 * @author cosmo
 */
public class SimpleSemaphore {

    private boolean aquired = false;
    private Thread currThread;
    private Thread releasedThread;
    private int pretendersCount = 0;

    public synchronized void aquire() throws InterruptedException {
        while ((Thread.currentThread() != currThread && aquired) || (pretendersCount > 0 && Thread.currentThread() == releasedThread)) {
            pretendersCount++;
            try {
                wait();
            } finally {
                pretendersCount--;
            }
        }
        aquired = true;
        currThread = Thread.currentThread();
    }

    public synchronized void release() {
        if (Thread.currentThread() == currThread) {
            aquired = false;
            currThread = null;
            releasedThread = Thread.currentThread();
            notifyAll();
        }
    }

}

该类中的关键是在aquire方法中进行测试,以查看线程获取是否是您想要的线程,所有其他线程都必须等待。因此,如果您有足够的信息来确定该线程,您可以选择从
aquire()返回的线程。

这是课程的作业吗?如果是,请添加一个家庭作业标记。相反,有很多方法可以在不使用
ReentrantLock
的情况下使事情公平:例如,您可以使用队列。@UmNyobe我理解,但需要某种形式的同步,如果他试图使用
synchronized
,它将无法满足公平性要求。@实际上,线程安全队列(例如ArrayBlockingQueue)使用可重入锁来控制公平性。默认情况下,ABQ是不公平的,只有当
true
作为构造函数参数传递时,ABQ才是公平的。@JohnVint和
ReentrantLock
本身是使用
AbstractQueuedSynchronizer
内的队列实现的。这是一个很大的工作,因为您基本上需要实现自己的特殊队列,但是在没有
ReentrantLock
@trutheality的情况下,这是可行的。这是一个很好的观点,我认为您天真地指的是Java提供的标准队列。
package nmscd.utils;

/**
 * A simple, non-reentrant, one permit, FAIR semaphore
 * @author cosmo
 */
public class SimpleSemaphore {

    private boolean aquired = false;
    private Thread currThread;
    private Thread releasedThread;
    private int pretendersCount = 0;

    public synchronized void aquire() throws InterruptedException {
        while ((Thread.currentThread() != currThread && aquired) || (pretendersCount > 0 && Thread.currentThread() == releasedThread)) {
            pretendersCount++;
            try {
                wait();
            } finally {
                pretendersCount--;
            }
        }
        aquired = true;
        currThread = Thread.currentThread();
    }

    public synchronized void release() {
        if (Thread.currentThread() == currThread) {
            aquired = false;
            currThread = null;
            releasedThread = Thread.currentThread();
            notifyAll();
        }
    }

}