通常,现有的主题可以帮助我解决问题,但现在我发现自己陷入困境。
我想在Java中使用并发来实现Prod / Cons。不使用现有API,因为它是出于学习目的。
My Producers阻止消费者使用队列中的消息(Holder),但我希望Producer和Consumers同时使用队列。
您可以运行我的示例,您将看到,在Producer添加时,Consumer等待锁定。但我希望消费者在添加消息后立即完成工作,而不是在制作人告诉他时。
我感到惊讶的是,我发现搜索P / C模式的所有例子都是我的(生产者阻止了消费者,这对我来说没有意义)
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
class Holder<T> {
private int capacity;
private Queue<T> items = new LinkedList<T>();
public Holder(int capacity) {
this.capacity = capacity;
}
public synchronized void addItem(T item) throws InterruptedException {
Thread.sleep(new Random().nextInt(2000));
while (isFull()) {
System.out.println("Holder FULL. adding operation is waiting... [" + item + "]");
this.wait();
}
System.out.println(items.size() + " -- holder +++ added " + item);
items.add(item);
this.notifyAll();
}
public T getItem() throws InterruptedException {
synchronized (this) {
while (isEmpty()) {
System.out.println("Holder EMPTY. getting operation is waiting...");
this.wait();
}
T next = items.poll();
System.out.println(items.size() + " -- holder --- removed " + next + " - remaining: " + items.size());
this.notifyAll();
return next;
}
}
private synchronized boolean isEmpty() {
return items.isEmpty();
}
private synchronized boolean isFull() {
return items.size() >= capacity;
}
}
class Producer implements Runnable {
public static final int GENERATED_ITEMS_COUNT = 10;
private int id;
private Holder<String> holder;
public Producer(int id, Holder<String> holder) {
this.id = id;
this.holder = holder;
}
@Override
public void run() {
try {
for (int i = 0; i < GENERATED_ITEMS_COUNT; i++) {
String produced = "Message " + i + " from [P" + id + "] " + System.nanoTime();
holder.addItem(produced);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
private Holder<String> holder;
public Consumer(Holder<String> hodler) {
this.holder = hodler;
}
@Override
public void run() {
while (true) {
try {
String consumed = holder.getItem();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ConsumerProducerApp {
public static void main(String[] args) throws InterruptedException {
Holder<String> coada = new Holder<String>(10);
Thread consumer = new Thread(new Consumer(coada));
consumer.start();
Executor executor = Executors.newCachedThreadPool();
for (int i = 1; i <= 9; i++) {
executor.execute(new Producer(i, coada));
}
}
}
编辑: 所以假设我们从这个等式中排除了Thread.sleep。如果我有100000个生产者,他们每个人都会发出消息。他们不是阻止我的消费者吗?因为持有人的常见锁定。 不管怎么样,也许是让我的消费者单独完成工作的另一种模式?从我的理解到现在,我的实施是正确的,我可能会尝试实现不可能的事情?
答案 0 :(得分:1)
在任何实际情况中,你需要有一个平衡数量的生产者和消费者,否则,生产者明显增多,应用程序迟早会崩溃,因为堆已经搞砸了尚未消耗的生产物品。 / p>
对此的一个解决方案是拥有像ArrayBlockingQueue
这样的有界队列。消费者和生产者在队列访问期间被阻塞了一小段时间,但如果生产者疯狂,队列的容量将耗尽,生产者将进入等待状态,因此消费者可以赶上然后
如果您对单个队列有很多并发访问权限,并认为小的阻塞时间总和相关,那么您可以使用像ConcurrentLinkedQueue
这样的非阻塞队列 - 不建议尝试实施这样的数据结构你自己。在这里,消费者和生产者可以同时访问队列,但是,如果你的生产者生产的速度比消费者处理项目的速度快,那么没有什么能保护你免于堆积崩溃......
答案 1 :(得分:0)
为了线程安全,消费者和生产者可能不会同时使用queur。但是从队列中添加或删除应该是超高速的。在一个现实的例子中,需要时间的是生成项目(例如获取网页)并使用它(例如解析它)。
您的sleep()
来电应该在同步区块之外:
public void addItem(T item) throws InterruptedException {
// simulating long work, not using the queue
Thread.sleep(new Random().nextInt(2000));
// long work done, now use the queue
synchronized (this) {
while (isFull()) {
System.out.println("Holder FULL. adding operation is waiting... [" + item + "]");
this.wait();
}
System.out.println(items.size() + " -- holder +++ added " + item);
items.add(item);
this.notifyAll();
}
}