我有以下设计:
有一个扩展TimerTask
的任务,它计划每分钟运行一次。
此任务将尝试从中央队列(作为单个使用者)获取项目并将其表示写入文件。
此外,还有多个生产者不时将物品放入中央队列。
我感兴趣的是,每次执行任务(run() method executed
)时,如果有项目,如果没有项目什么都不做,它将从队列中提取所有项目。< / p>
如果队列已满,生产者应该在队列中休眠。
我对此问题的解决方案是:
创建扩展TimerTask的ExtractTask。 ExtractTask将包含BlockingQueue。 每个生产者将通过执行方法getQueue()接收对队列实例的引用。 生产者将执行BlockingQueue.put()方法。 消费者将在run()中执行BlockingQueue.poll()方法。
你能建议更好的设计吗?我的设计是否包含任何有问题的情景案例?这个设计可能遇到的任何同步问题?
答案 0 :(得分:2)
我会:
除此之外,你已经得到了它。
如果您愿意承担对Spring的依赖风险,您应该查看Spring Integration。您描述的所有组件都在那里。您还可以使用许多其他框架来解决问题,例如Camel或Akka;我的主要观点是如果你不是必须的话,不要自己维护这段代码。
免责声明:我有点biased about Spring Integration
答案 1 :(得分:1)
设计似乎很好。这里没有太多细节,很难确定。我建议将所有依赖项注入计时器任务。
此外,你可能在没有太多自定义代码的Apache Camel中实现这一点。见https://camel.apache.org/timer.html
答案 2 :(得分:1)
既然您已经询问了设计,我会建议一些事情:
答案 3 :(得分:1)
根据您的设计,我可以想到下面的内容。 ConsumerTask可以使用泛型,但我很难弄清楚如何对Producer线程做同样的事情。生产者和消费者都对生产/消费的物品数量有限制。从TimerTask逻辑到必须取消TimerTask本身的run()方法中的定时器才能停止。在这种情况下,只能使用POISON PILL方法来关闭。 如果使用Executors.newSingleThreadExecutor()或scheduledThreadPoolExecutor(),则shutdown()和shutdownNow()方法可用于停止生产者或使用者。虽然TimerTask是检查ConcurrentQueue工作的一个很好的例子,但它不会在生产系统中使用。
修改强> 将常规功能添加到Producer线程。构造函数现在接受一个模板类,该类实现将项添加到队列的方法。我已经定义了一个抽象类AddItem,它包含addItem()方法,只要Producer想要将项添加到Queue中就会调用它。
import java.util.Date;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class ConsumerTask<T> extends TimerTask {
Timer timer;
ConcurrentLinkedQueue<T> itemQueue;
AtomicLong count = new AtomicLong(0);
final long limit;
public ConsumerTask(ConcurrentLinkedQueue<T> itemQ, long lim, int seconds) {
limit = lim;
timer = new Timer();
timer.scheduleAtFixedRate(this, new Date(), seconds * 1000);
itemQueue = itemQ;
}
public void run() {
T item = itemQueue.peek();
if (item != null) {
if (count.incrementAndGet() <= limit) {
System.out.println("Extracting Item : " + itemQueue.poll());
} else {
System.out
.println("Consumed : " + (count.get() - 1) + " items");
timer.cancel();
}
}
}
public static void main(String args[]) throws InterruptedException {
ConcurrentLinkedQueue<Integer> itemQ = new ConcurrentLinkedQueue<Integer>();
ConsumerTask<Integer> ct = new ConsumerTask<Integer>(itemQ, 10, 1);
new Thread(new Producer<Integer>(itemQ, new IntegerAddItem(itemQ), 20))
.start();
new Thread(ct).start();
}
}
abstract class AddItem<T> {
ConcurrentLinkedQueue<T> itemQ;
T t;
public AddItem(ConcurrentLinkedQueue<T> itemQ) {
this.itemQ = itemQ;
}
abstract boolean addItem();
public boolean addItem(T t) {
return itemQ.add(t);
}
}
class IntegerAddItem extends AddItem<Integer> {
public IntegerAddItem(ConcurrentLinkedQueue<Integer> itemQ) {
super(itemQ);
}
AtomicInteger item = new AtomicInteger(0);
@Override
boolean addItem() {
return addItem(item.incrementAndGet());
}
}
class Producer<T> implements Runnable {
private final ConcurrentLinkedQueue<T> itemQueue;
AtomicInteger item = new AtomicInteger(0);
AtomicLong count = new AtomicLong(0);
AddItem<T> addMethod;
final long limit;
public Producer(ConcurrentLinkedQueue<T> itemQ, AddItem<T> addMethod,
long limit) {
itemQueue = itemQ;
this.limit = limit;
this.addMethod = addMethod;
}
public void run() {
while (count.getAndIncrement() < limit) {
addMethod.addItem();
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Thread.currentThread().interrupt();
}
}
}
}
答案 4 :(得分:0)
你说消费者会在执行计时器时提取所有项目。
你应该注意从队列中提取所有项目的操作不是阻塞操作,它是poll()
阻塞方法调用的重复,这意味着在提取项目时
生产者将能够将项目添加到队列中。