我有PriorityBlockingQueue
。单个线程一次从该队列中消耗一条消息并对其进行处理。另外几个线程正在将消息插入队列。生产者线程为他们提交的每条消息分配一个完整的优先级。静态AtomicLong
用于为每个消息分配唯一的,单调递增的ID。队列的Comparator
首先按此优先级排序消息,然后按ID排序相同的优先级消息(最低ID排在第一位。)
问题:有时一个制作人会提交大量的邮件。然后,这使得其他制作者处理他们的消息。我想做的是让生产者之间的消费者循环使用相同优先级的消息(同时仍然按提交顺序处理单个生产者的相同优先级消息)。但我无法弄清楚如何编写Comparator
来执行此操作。
我考虑的另一个选择是为每个制作人提供一个单独的队列。但是,我不认为这可行,因为我不知道单线程在多个队列上等待的任何方法。
答案 0 :(得分:3)
对于每个制作人来说,我觉得用一个Queue
来实现这个更直接。一个线程不能在多个Queue
上等待,但您可以将所有Queue
组合成一个帮助程序类,以便它不需要。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.concurrent.GuardedBy;
public class RoundRobin<P, E> {
private final Lock lock = new ReentrantLock();
private final Condition added = lock.newCondition();
@GuardedBy("lock") private final Map<P, Queue<E>> queues = new LinkedHashMap<>();
public boolean add(P producer, E item) {
lock.lock();
try {
if (!queues.containsKey(producer)) {
queues.put(producer, new PriorityBlockingQueue<>());
}
added.signalAll();
return queues.get(producer).add(item);
} finally {
lock.unlock();
}
}
public Iterator<E> roundRobinIterator() {
return new Iterator<E>() {
private Iterator<? extends Queue<E>> i = null;
private boolean singlePass = true;
@Override
public boolean hasNext() {
return true;
}
@Override
public E next() {
lock.lock();
try {
while (true) {
if (i == null || !i.hasNext()) {
i = queues.values().iterator();
singlePass = true;
}
while (i.hasNext()) {
Queue<E> q = i.next();
if (!q.isEmpty()) {
if (singlePass) {
// copy the iterator to prevent
// ConcurrentModificationExceptions
singlePass = false;
i = copy(i);
}
return q.poll();
}
}
if (singlePass) {
// If singlePass is true then we just checked every
// queue and they were all empty.
// Wait for another element to be added.
added.await();
}
}
} catch (InterruptedException e) {
throw new NoSuchElementException(e.getMessage());
} finally {
lock.unlock();
}
}
private <T> Iterator<? extends T> copy(Iterator<? extends T> i) {
List<T> copy = new ArrayList<>();
while (i.hasNext()) {
copy.add(i.next());
}
return copy.iterator();
}
};
}
}
答案 1 :(得分:2)
我想我会做那样的事情:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class RRQueue<M> {
private final ThreadLocal<Queue<M>> threadQueue = new ThreadLocal<>();
private final List<Queue<M>> queues;
private int current = 0;
public RRQueue() {
this.queues = new ArrayList<>();
}
public synchronized void add(M msg) {
Queue<M> queue = threadQueue.get();
if (queue == null) {
queue = new LinkedList<>(); // or whatever
queues.add(queue);
threadQueue.set(queue);
}
queue.add(msg);
notify();
}
public synchronized M get() throws InterruptedException {
while (true) {
for (int i = 0; i < queues.size(); ++i) {
Queue<M> queue = queues.get(current);
current = (current+1)%queues.size();
if (!queue.isEmpty()) {
return queue.remove();
}
}
wait();
}
}
}
答案 2 :(得分:0)
这就是你如何分配ID。分别将它们 N 分配,其中 N 是生产者的数量,并将每个生产者的索引添加到其中。然后按顺序读取它们将产生循环顺序。你需要做一些记账才能知道何时增加基础ID,当你到达任何 x 的 Nx-1 时会发生这种情况。
答案 3 :(得分:0)
可以使用如下PriorityBlockingQueue在N个生产者之间进行轮循。
为每个生产者维护N个AtomicInteger计数器,并在生产者计数器相等的情况下维护全局计数器作为决胜局。
在为生产者和全局计数器添加到Q增量计数器时,将其存储到Q对象中。 Q的比较器将根据生产者计数器1st进行排序,然后根据存储在Q对象中的全局计数器值进行排序。
但是,当某个生产者的对象在Q中的某个时间为空时,相应的计数器就会落后,并且当对象开始进入时它将开始占用Q。
为避免这种情况,请保留易失性变量,该变量在出队时将使用对象的生产者计数器进行更新。在入队期间增加生产者计数器后,如果该值小于易失变量中最后一个已出队的计数器,则将该计数器重置为该值+1。