【问题标题】:How to write simple fair semaphore?如何编写简单的公平信号量?
【发布时间】:2012-03-29 15:43:05
【问题描述】:

我发现信号量的简单实现(我的 CustomSemaphore),据我所知,这是“不公平”的,因为进入安全块时只能进入第一个线程(我不确定)。 我怎样才能写出公平的信号量(并发new Semaphore(1, true);的类比)

   public class SimpleSemaphoreSample2 {
    CustomSemaphore cSem = new CustomSemaphore(1);

    public static void main(String[] args) {
        SimpleSemaphoreSample2 main = new SimpleSemaphoreSample2();
        Semaphore sem = new Semaphore(1, true);
        Thread thrdA = new Thread(main.new SyncOutput(sem, "Thread1"), "Thread1");
        Thread thrdB = new Thread(main.new SyncOutput(sem, "Thread2"), "Thread2");

        thrdA.start();
        thrdB.start();

        try {
            thrdB.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("END");
    }

    class SyncOutput implements Runnable {
        private Semaphore sem;
        private String msg;

        public SyncOutput(Semaphore s, String m) {
            sem = s;
            msg = m;
        }

        @Override
        public void run() {
            while (true) {
                try {
//                  sem.acquire();
                    cSem.acquire();
                    System.out.println("Before");
                    Thread.sleep(500);
                    System.out.println(msg);
                    Thread.sleep(500);
                    System.out.println("After");
                    Thread.sleep(500);
                } catch (Exception exc) {
                    exc.printStackTrace();
                }
//              sem.release();
                cSem.release();
            }
        }
    }

    public class CustomSemaphore {
        private int counter;

        public CustomSemaphore() {
            this(0);
        }

        public CustomSemaphore(int i) {
            if (i < 0)
                throw new IllegalArgumentException(i + " < 0");
            counter = i;
        }

        public synchronized void release() {
            if (counter == 0) {
                this.notify();
            }
            counter++;
        }

        public synchronized void acquire() throws InterruptedException {
            while (counter == 0) {
                this.wait();
            }
            counter--;
        }
    }
}
enter code here

【问题讨论】:

  • 这是课程作业吗?如果是,请添加作业标签。

标签: java concurrency semaphore


【解决方案1】:

您的信号量不公平,因为线程可能永远等待。考虑一个用于由 3 个线程写入值的互斥体(二进制信号量)。 T1 获取,T2 等待,T3 等待。现在在发布期间,您通知 T2 和 T3 之间的一个接收信号量(比如说 T2)。现在T1回来等待。当 T2 通知时,T1 接受它。它可以发生尽可能多的次数,而 T3 永远不会有信号量。

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

【讨论】:

    【解决方案2】:

    根据Java Concurrency In Practice 声明,

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

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

    true 作为构造函数参数告诉 ReentrantLock 是公平

    根据@trutheality 的评论进行编辑

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

    【讨论】:

    • 相反,有很多方法可以在不使用ReentrantLock 的情况下使事情变得公平:例如,您可以使用队列。
    • @UmNyobe 我明白这一点,但需要某种形式的同步,如果他尝试使用synchronized,它将无法满足公平性要求。
    • @trutheality 线程安全队列,例如 ArrayBlockingQueue,使用 ReentrantLock 来控制公平性。 ABQ 默认情况下是不公平的,只有当true 作为构造函数参数传递时才公平。
    • @JohnVint 而ReentrantLock 本身是使用AbstractQueuedSynchronizer 内部的队列实现的。这是很多工作,因为您基本上需要实现自己的特殊队列,但是没有ReentrantLock 也是可行的。
    • @trutheality 说得好,我以为您天真地指的是 Java 提供的标准队列。
    【解决方案3】:

    我有一个可重入信号量的示例,但只为 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() 返回的线程

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-30
      • 2013-07-23
      • 2012-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多