发布者/订阅者模式的并发实现

时间:2013-05-31 16:20:08

标签: java concurrency publish-subscribe java.util.concurrent

我想使用Java实现各种发布者/订阅者模式,并且目前用完了想法。

有1个发布者和N个订阅者,发布者发布对象,然后每个订阅者需要一次处理每个对象,并且只按正确的顺序处理一次。发布者和每个订阅者都在自己的主题中运行。

在我最初的实现中,每个订阅者都有自己的阻塞队列,发布者将对象放入每个订阅者的队列中。这可以正常工作,但如果任何订户的队列已满,将阻止发布者。这导致性能下降,因为每个订户在处理对象时花费不同的时间。

然后在另一个实现中,发布者将对象保存在自己的队列中。与对象一起,AtomicInteger计数器与其关联,其中包含订户数。然后,每个订户查看队列并减少计数器,并在计数器达到零时将其从队列中删除。

通过这种方式,发布者可以免于阻止,但现在订阅者需要等待彼此处理对象,从队列中删除对象,然后才能查看下一个对象。

有没有更好的方法呢?我认为这应该是一种非常常见的模式。

3 个答案:

答案 0 :(得分:0)

你的"很多队列"实施是要走的路。我不认为您需要关注阻止生产者的一个完整队列,因为完成的总时间不会受到影响。假设你有三个消费者,两个消费者以每秒1个的速度消费,第三个消费者以每五秒1个的速度消费,同时生产者以每两秒一个的速度消费。最终第三个队列将填满,因此生产者将阻止它,并且还将停止将项目放在第一个和第二个队列中。有很多方法可以解决这个问题,但他们并没有改变第三个消费者总是成为瓶颈的事实。如果您正在生产/消费100件商品,那么由于第三个消费者(5件次100件商品),这将至少需要500秒,即使第一个和第二个消费者在200秒后完成也是如此(因为你已经做了一些聪明的事情,允许生产者在第三个队列满了之后继续填充他们的队列)或者他们在500秒后完成(因为生产者在第三个队列上被阻止)。

答案 1 :(得分:0)

肯定

  

每个订阅者都有自己的阻塞队列,发布者将对象放入每个订阅者的队列中。

这是要走的路。 您可以使用线程方法将其放入队列中...因此,如果一个队列已满,则发布者不会等待..

例如。

s1 s2 s3是订阅者,addToQueue是每个订阅者中添加到相应队列的方法。 addQueue方法等待队列非空。所以拨打addQueue将是阻止呼叫ideally synchronised code ...

然后在发布商中,您可以执行与以下代码类似的操作

注意:代码可能不是处于工作状态,但是应该给你一些想法。

List<subscriber> slist;// Assume its initialised
public void publish(final String message){

    for (final subscriber s: slist){


          Thread t=new Thread(new Runnable(){
             public void run(){
                s.addToQueue(message);
             }
           });

      t.start();
    }

}

答案 2 :(得分:0)

  

有1个发布者和N个订阅者,发布者发布对象,然后每个订阅者需要一次处理每个对象,并且只按正确的顺序处理一次。发布者和每个订阅者都在自己的主题中运行。

我会改变这种架构。我最初考虑每个用户的队列,但我不喜欢这种机制。例如,如果第一个订阅者需要更长的时间来运行,那么所有作业将最终进入该队列,并且您将只执行1个工作线程。

由于你必须按顺序运行订阅者,我会有一个线程池,它通过所有订阅者运行每条消息。对订户的呼叫需要是可重入的,这可能是不可能的。

所以你将拥有一个包含10个线程的池(比方说),每个线程都从发布者的队列中出列,并执行以下操作:

public void run() {
    while (!shutdown && !Thread.currentThread().isInterrupted()) {
        Article article = publisherQueue.take();
        for (Subscriber subscriber : subscriberList) {
           subscriber.process(article);
        }
    }
}