我的生产者和消费者与BlockingQueue
有关。
消费者从队列中等待记录并处理它:
Record r = mQueue.take();
process(r);
我需要从其他线程暂停这个过程一段时间。如何实现呢?
现在我认为实现它,但它看起来像是一个糟糕的解决方案:
private Object mLock = new Object();
private boolean mLocked = false;
public void lock() {
mLocked = true;
}
public void unlock() {
mLocked = false;
mLock.notify();
}
public void run() {
....
Record r = mQueue.take();
if (mLocked) {
mLock.wait();
}
process(r);
}
答案 0 :(得分:2)
您可以使用java.util.concurrent.locks.Condition
Java docs根据相同条件暂停一段时间。
这种方法看起来很干净, ReentrantLock机制的吞吐量比同步更好。请阅读以下IBM article
的摘录作为奖励,ReentrantLock的实施更具可扩展性 争用比当前执行同步。 (它 可能会对竞争性能进行改进 在未来版本的JVM中同步。)这意味着什么时候 许多线程都争夺同一个锁,总数 ReentrantLock的吞吐量通常会好于 同步。
BlockingQueue以解决生产者 - 消费者问题而闻名,它还使用Condition
进行等待。
请参阅以下来自Java doc' s Condition的示例,该示例是生产者 - 消费者模式的示例实现。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
进一步阅读:
答案 1 :(得分:2)
我认为您的解决方案简单而优雅,并认为您应该对其进行一些修改。我建议的修改是synchronization。
没有它,线程干扰和内存一致性错误可能(并且经常会发生)。最重要的是,你不能拥有wait
或notify
你不拥有的锁(如果你在synchronized
区块中拥有它,你就拥有它...)。修复很简单,只需在等待/通知的位置添加mLock
同步块。此外,当您从其他主题更改mLocked
时,您需要将其标记为volatile。
private Object mLock = new Object();
private volatile boolean mLocked = false;
public void lock() {
mLocked = true;
}
public void unlock() {
synchronized(mlock) {
mLocked = false;
mLock.notify();
}
}
public void run() {
....
Record r = mQueue.take();
synchronized(mLock) {
while (mLocked) {
mLock.wait();
}
}
process(r);
}
答案 2 :(得分:1)
创建一个扩展BlockingQueue
实现的新类。添加两个新方法pause()
和unpause()
。如果需要,请考虑paused
标记并使用其他blockingQueue2
等待(在我的示例中仅使用take()
方法,而不是put()
):
public class BlockingQueueWithPause<E> extends LinkedBlockingQueue<E> {
private static final long serialVersionUID = 184661285402L;
private Object lock1 = new Object();//used in pause() and in take()
private Object lock2 = new Object();//used in pause() and unpause()
//@GuardedBy("lock")
private volatile boolean paused;
private LinkedBlockingQueue<Object> blockingQueue2 = new LinkedBlockingQueue<Object>();
public void pause() {
if (!paused) {
synchronized (lock1) {
synchronized (lock2) {
if (!paused) {
paused = true;
blockingQueue2.removeAll();//make sure it is empty, e.g after successive calls to pause() and unpause() without any consumers it will remain unempty
}
}
}
}
}
public void unpause() throws InterruptedException {
if (paused) {
synchronized (lock2) {
paused = false;
blockingQueue2.put(new Object());//release waiting thread, if there is one
}
}
}
@Override
public E take() throws InterruptedException {
E result = super.take();
if (paused) {
synchronized (lock1) {//this guarantees that a single thread will be in the synchronized block, all other threads will be waiting
if (paused) {
blockingQueue2.take();
}
}
}
return result;
}
//TODO override similarly the poll() method.
}