我想知道是否可以使用Semaphore来实现阻塞队列?
在下面的代码中,我使用一个Semaphore来保护关键部分,再使用两个Semaphore对象来跟踪空槽和填充对象的数量。
public class BlockingQueue {
private List<Object> queue = new LinkedList<Object>();
private int limit;
private Semaphore slots; // semaphore for empty slots
private Semaphore objs; // semaphore for filled slots
private Semaphore mutex; // for the critical section
public BlockingQueue(int limit) {
this.limit = limit;
this.slots = new Semaphore(limit); // initial empty slot = capacity
this.objs = new Semaphore(0);
this.mutex = new Semaphore(1);
}
private void enqueue(Object o) throws InterruptedException {
slots.acquire();
mutex.acquire(); // critical section starts
queue.add(o);
mutex.release(); // critical section ends
objs.release();
}
private Object dequeue() throws InterruptedException {
objs.acquire();
mutex.acquire(); // critical section starts
Object o = queue.remove(0);
mutex.release(); // critical section ends
slots.release();
return o;
}
}
答案 0 :(得分:2)
添加到之前的评论 - 我们可以同意您的代码有效(这是一个众所周知的算法),特别是您保护LinkedList是正确的,因为它不是内部线程安全的。
但是,如果将代码与java util实现进行比较http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/concurrent/ArrayBlockingQueue.java/?v=source 它可能会提出一些要考虑的要点:
Google请讨论“ReentrantLock与Binary Semaphore”:他们都创建了互斥锁&amp;保护关键部分,但前者更好地描述了您的意图,而且可能更容易进行日常维护。例如,同事程序员不能通过没有获取它的线程意外释放ReentrantLock
谷歌讨论“信号量与条件变量”:两者都允许你“等待某些东西变得可用”,但条件变量可能更普遍,而且你可以将所有条件绑定到一个锁(就像java util代码那样)。我认为这对性能有一些小的影响,加上你需要接近未来需求的方式,如中断,超时,崩溃。这不会使您的代码“错误”,这只是您需要考虑的事情。
答案 1 :(得分:1)
如果没有测试,我会说这很有效。 但是,每个release()都会通知在acquire()中阻塞的线程。因此,您实际上至少具有与重入锁定+条件相同的成本,可能更糟糕,因为有2个获取和2个release()调用。
答案 2 :(得分:0)
信号量是另一种实现互斥和同步的方式。 二进制信号量或互斥锁可用于锁定和解锁临界区。 通用信号量或计数信号量可用于跟踪空闲槽位和占用槽位。