【问题标题】:Observer Design Pattern观察者设计模式
【发布时间】:2010-12-24 04:10:26
【问题描述】:

观察者设计模式中,主体通过调用每个观察者的update()操作来通知所有观察者。一种方法是

void notify() {
   for (observer: observers) {
      observer.update(this);
   }
}

但这里的问题是每个观察者都按顺序更新,并且观察者的更新操作可能不会在所有观察者更新之前被调用。如果有一个观察者有一个用于更新的无限循环,那么它之后的所有观察者将永远不会被通知。

问题:

  1. 有没有办法解决这个问题?
  2. 如果是这样,什么是一个很好的例子?

【问题讨论】:

    标签: java design-patterns observer-pattern


    【解决方案1】:

    经典设计模式不涉及并行和线程。您必须为 N 个观察者生成 N 个线程。不过要小心,因为他们与 this 的交互必须以线程安全的方式完成。

    【讨论】:

      【解决方案2】:

      问题在于无限循环,而不是一个接一个的通知。

      如果您希望事情同时更新,您需要在不同的线程上触发事情 - 在这种情况下,每个侦听器都需要与其他侦听器同步才能访问触发事件的对象。

      抱怨一个无限循环阻止其他更新的发生就像抱怨一个锁然后进入一个无限循环阻止其他人访问锁定的对象 - 问题是无限循环,而不是锁管理器。

      【讨论】:

      • +1 用于解决问题而不是症状。否则你会得到越来越疯狂的附加修复,使维护变得不可能。
      • 我同意。我只是想看看是否有办法解决这个问题,如果它发生。谢谢。
      【解决方案3】:

      您可以使用 java.utils.concurrent.Executors.newFixedThreadPool(int nThreads) 方法,然后调用 invokeAll 方法(也可以使用带有 timout 的方法以避免无限循环)。

      您可以更改循环以添加一个 Callable 类,该类接受“observer”和“this”,然后在“call”方法中调用 update 方法。

      Take a look at this package for more info.

      这是我所说的快速而肮脏的实现:

      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.Callable;
      import java.util.concurrent.CopyOnWriteArrayList;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.TimeUnit;
      
      public class Main
      {
          private Main()
          {
          }
      
          public static void main(final String[] argv)
          {
              final Watched       watched;
              final List<Watcher> watchers;
      
              watched = new Watched();
              watchers = makeWatchers(watched, 10);
              watched.notifyWatchers(9);
          }
      
          private static List<Watcher> makeWatchers(final Watched watched,
                                                    final int     count)
          {
              final List<Watcher> watchers;
      
              watchers = new ArrayList<Watcher>(count);
      
              for(int i = 0; i < count; i++)
              {
                  final Watcher watcher;
      
                  watcher = new Watcher(i + 1);
                  watched.addWatcher(watcher);
                  watchers.add(watcher);
              }
      
              return (watchers);
          }
      }
      
      class Watched
      {
          private final List<Watcher> watchers;
      
          {
              watchers = new ArrayList<Watcher>();
          }
      
          public void addWatcher(final Watcher watcher)
          {
              watchers.add(watcher);
          }
      
          public void notifyWatchers(final int seconds)
          {
              final List<Watcher>         currentWatchers;
              final List<WatcherCallable> callables;
              final ExecutorService       service;
      
              currentWatchers = new CopyOnWriteArrayList<Watcher>(watchers);
              callables       = new ArrayList<WatcherCallable>(currentWatchers.size());
      
              for(final Watcher watcher : currentWatchers)
              {
                  final WatcherCallable callable;
      
                  callable = new WatcherCallable(watcher);
                  callables.add(callable);
              }
      
              service = Executors.newFixedThreadPool(callables.size());
      
              try
              {
                  final boolean value;
      
                  service.invokeAll(callables, seconds, TimeUnit.SECONDS);
                  value = service.awaitTermination(seconds, TimeUnit.SECONDS);
                  System.out.println("done: " + value);
              }
              catch (InterruptedException ex)
              {
              }
      
              service.shutdown();
              System.out.println("leaving");
          }
      
          private class WatcherCallable
              implements Callable<Void>
          {
              private final Watcher watcher;
      
              WatcherCallable(final Watcher w)
              {
                  watcher = w;
              }
      
              public Void call()
              {
                  watcher.update(Watched.this);
                  return (null);
              }
          }
      }
      
      class Watcher
      {
          private final int value;
      
          Watcher(final int val)
          {
              value = val;
          }
      
          public void update(final Watched watched)
          {
              try
              {
                  Thread.sleep(value * 1000);
              }
              catch (InterruptedException ex)
              {
                  System.out.println(value + "interupted");
              }
      
              System.out.println(value + " done");
          }
      }
      

      【讨论】:

        【解决方案4】:

        所有观察者都会收到通知,这就是您获得的全部保证。

        如果你想实现一些花哨的排序,你可以这样做:

        • 只连接一个观察者;
        • 让这个主要观察者按照您在代码中定义的顺序或通过其他方式通知他的朋友。

        这让你远离了经典的观察者模式,因为你的听众是硬连线的,但如果这是你需要的……就去做吧!

        【讨论】:

          【解决方案5】:

          如果你有一个“无限循环”的观察者,那它就不再是真正的观察者模式了。

          您可以为每个观察者触发不同的线程,但必须禁止观察者更改被观察对象的状态。

          最简单(也是最愚蠢)的方法就是以您的示例并使其线程化。

          void notify() {
             for (observer: observers) {
                new Thread(){
                    public static void run() {
                        observer.update(this);
                    } 
                }.start();
             }
          }
          

          (这是手工编码的,未经测试,可能有一个或五个错误——无论如何这都是个坏主意)

          这样做的问题是它会使你的机器变得笨重,因为它必须一次分配一堆新线程。

          因此,要解决所有线程同时启动的问题,请使用 ThreadPoolExecutor,因为它会 A) 回收线程,并且 B) 可以限制运行的最大线程数。

          在“永远循环”的情况下,这不是确定性的,因为每个永远循环都会永久占用池中的一个线程。

          最好不要让它们永远循环,或者如果必须,让它们创建自己的线程。

          如果您必须支持无法更改的类,但您可以确定哪些会快速运行,哪些会“永远”运行(在计算机术语中,我认为这相当于一两秒以上),那么您可以使用像这样的循环:

          void notify() {
             for (observer: observers) {
                if(willUpdateQuickly(observer))
                    observer.update(this);
                else
                    new Thread(){
                        public static void run() {
                            observer.update(this);
                        } 
                    }.start();
             }
          }
          

          嘿,如果它真的“永远循环”,它会为每个通知消耗一个线程吗?听起来您可能需要在设计上花费更多时间。

          【讨论】:

            【解决方案6】:

            1.有没有办法解决这个问题?

            是的,确保观察员工作正常并及时返回。

            2.有人可以举个例子来解释一下吗?

            当然:

            class ObserverImpl implements Observer {
                 public void update( Object state ) {
                        // remove the infinite loop.
                        //while( true ) {
                        //   doSomething();
                        //}
            
                        // and use some kind of control:
                        int iterationControl = 100;
                        int currentIteration = 0;
                        while( curentIteration++ < iterationControl ) {
                             doSomething();
                        }
                 }
                 private void doSomething(){}
            }
            

            这可以防止给定循环无限循环(如果有意义,它应该最多运行 100 次)

            其他机制是在第二个线程中启动新任务,但如果它进入无限循环,最终会消耗所有系统内存:

            class ObserverImpl implements Observer {
                 public void update( Object state ) {
                     new Thread( new Runnable(){ 
                         public void run() {
                             while( true ) {
                                 doSomething();
                             }
                         }
                      }).start();
                 }
                 private void doSomething(){}
            }
            

            这将使那个观察者实例立即返回,但这只是一种错觉,你实际上要做的是避免无限循环。

            最后,如果您的观察员工作正常,但您只想尽快通知他们,您可以看看这个相关问题:Invoke a code after all mouse event listeners are executed.

            【讨论】:

              【解决方案7】:

              我更关心观察者抛出异常,而不是它无限循环。您当前的实现不会在此类事件中通知剩余的观察者。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-11-25
                • 2011-09-25
                • 2010-12-31
                • 2011-03-22
                • 2020-12-28
                • 1970-01-01
                • 2013-08-03
                • 1970-01-01
                相关资源
                最近更新 更多