并发观察者模式

时间:2014-01-16 22:35:50

标签: java multithreading concurrency observer-pattern

如果我遇到以下情况:

ObserverA,ObserverB,ObserverC都继承自AbstractObserver。

我创建了一个观察者列表:

List<AbstractObserver> list = new ArrayList<AbstractObserver>();
list.add(new ObserverA());
list.add(new ObserverB());
list.add(new ObserverC());

使用以下方法的某种处理程序在“MAIN”线程中运行:

public void eat(Food item) {
     for(AbstractObserver o : list) {
          o.eatFood(item);
     }
}

public void drink(Coffee cup) {
     for(AbstractObserver o : list) {
          o.drinkCoffee(cup);
     }
}

我如何设计一个系统,我可以在不同的线程中运行每个eatFood和drinkCoffee方法的观察者?具体来说,当“MAIN”线程收到事件(饮料或吃方法被调用)时,如何在ObserverA,ObserverB和ObserverC中运行自己的线程中的eatFood或drinkCoffee方法?

我想为每个AbstractObserver子类实例设置不同的线程,因为目前我正在按顺序通知每个观察者,这可能会导致延迟。

3 个答案:

答案 0 :(得分:3)

在这里做一些简化的假设,你不关心在吃/喝结束时得到通知,你也可以使用执行器框架将工作扔到队列中:

  // declare the work queue
   private final Executor workQueue = Executors.newCachedThreadPool(); 




   // when you want to eat, schedule a bunch of 'eating' jobs
       public void eat(final Food item){
          for (final AbstractObserver o: list) {
             workQueue.execute(new Runnable() {

                @Override
                public void run() {
                   o.eatFood(item); // runs in background thread
                }
             });
          }
       }

退出程序时,必须关闭执行程序:

   workQueue.shutdown();

答案 1 :(得分:2)

我不是这方面的专家,但也许你可以使用Producer-consumer设置。在这里,生产者,即观察到的实体,可以在其自己的线程中的队列上添加通知,消费者(此处的观察者)将从同一队列获取,但是在其自己的线程上。

答案 2 :(得分:2)

详细说明Hovercraft的答案,观察者的基本实现可能如下所示:

class ObserverA implements Runnable {
    private final BlockingQueue<Food> queue = new ArrayBlockingQueue<> ();

    public void eatFood(Food f) {
        queue.add(f);
    }

    public void run() {
        try {
            while (true) {
                Food f = queue.take(); //blocks until some food is on the queue
                //do what you have to do with that food
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            //and exit
        }
    }
}

因此,调用eatFood的代码将立即从该方法返回,而不会阻止您的主线程。

你显然需要直接向观察者分配一个线程:new Thread(observerA).start();或者通过ExecutorService,这可能更容易和更可取。


或者,您可以在“观察”对象级别创建线程:

private static final ExecutorService fireObservers = Executors.newFixedThreadPool(10);

public void eat(Food food) {
    for (AbstractObserver o : observers) {
        //(i)  if an observer gets stuck, the others can still make progress
        //(ii) if an observer throws an exception, a new thread will be created
        Future<?> f = fireObservers.submit(() -> o.dataChanged(food));
        fireObservers.submit(new Callable<Void>() {
            @Override public Void call() throws Exception {
                try {
                    f.get(1, TimeUnit.SECONDS);
                } catch (TimeoutException e) {
                    logger.warn("Slow observer {} has not processed food {} in one second", o, food);
                } catch (ExecutionException e) {
                    logger.error("Observer " + o + " has thrown exception on food " + food, e.getCause());
                }
                return null;
            }
        });
    }
}

(我主要是从here复制粘贴 - 你可能需要根据自己的需要调整它。)