Java中的非重入非阻塞信号量

时间:2012-09-26 08:16:54

标签: java concurrency semaphore nonblocking

我需要一个具有以下功能的信号量:

  1. 它应该是非阻塞的,即如果线程无法获得许可 它应该更进一步,而不是等待
  2. 它应该是非等容的,即如果相同的线程进入 保护一段代码两次它应该拿走两个许可证而不是 一个
  3. 我写了以下代码:

    public class SimpleSemaphore
    {
    
        private int permits;
    
        private AtomicLong counter = new AtomicLong();
    
        SimpleSemaphore(int permits)
        {
            this.permits = permits;
        }
    
        boolean acquire()
        {
    
            if (counter.incrementAndGet() < permits)
            {
                return true;
            }
            else
            {
                counter.decrementAndGet();
                return false;
            }
    
        }
    
        void release()
        {
            counter.decrementAndGet();
    
        }
    }
    

    另一种选择是这个信号量:

    public class EasySemaphore
    {
    
        private int permits;
    
        private AtomicLong counter = new AtomicLong();
    
        EasySemaphore(int permits)
        {
            this.permits = permits;
        }
    
        boolean acquire()
        {
            long index = counter.get();
    
            if (index < permits)
            {
                if (counter.compareAndSet(index, index + 1))
                {
                    return true;
                }
            }
    
            return false;
        }
    
        void release()
        {
            counter.decrementAndGet();
        }
    }
    

    这两个实现是否是线程安全且正确的? 哪一个更好? 你会怎么做这个任务?

2 个答案:

答案 0 :(得分:7)

java.util.concurrent.Semaphore是不是已经完成了所有这些?

它具有tryAcquire非阻塞获取,并且它保留了剩余许可的简单计数(其中相同的线程可以取出多个)。

答案 1 :(得分:0)

我会说第二个更好,因为计数器永远不会大于0(并且效率稍高)

我会使用一个循环,否则当剩下许可证时你可以让方法失败。

public class EasySemaphore {
    private final AtomicInteger counter;

    EasySemaphore(int permits) {
        counter = new AtomicInteger(permits);
    }

    boolean acquire() {
        // highly unlikely to loop more than once.
        while(true) {
            int count = counter.get();
            if (count <= 0) return false;
            if (counter.compareAndSet(count, count -1)) 
                return true;
        }
    }

    void release() {
        counter.incrementAndGet();
    }
}