2009-12-07 137 views
4

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

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

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

问:

  1. 有没有办法来解决这个问题呢?
  2. 如果是这样会是一个很好的例子吗?

回答

10

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

20

问题是无限循环,而不是一个后 - 其他通知。

如果你想要的东西同时更新,你需要火的东西掉在不同的线程 - 在这种情况下,每个听众都需要为了访问触发事件的对象与他人同步。

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

+0

+1为解决问题而不是症状。否则,你会越来越疯狂的附加修补程序,使维护变得不可能。 – reccles 2009-12-07 21:20:06

+0

我同意。我只是想看看是否有办法解决这个问题。谢谢。 – suprasad 2009-12-08 01:07:00

5

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

你会改变你的循环添加一个类,它是可调用的,是以“观察员”和“本”,然后调用更新方法在“通话”的方法。

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"); 
    } 
} 
0

所有观察员获得通知,这就是你得到了保证。

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

  • 连接只是一个单一的观察员;
  • 有这个主要观察者以您在代码或其他方式定义的顺序通知他的朋友。

这会让你远离经典的观察者模式,因为你的听众是硬连线的,但如果这是你需要的......做到这一点!

0

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

你可以发射不同的线程每个观察者,但观察者必须从观察对象上改变国家被禁止。

最简单的(和愚蠢的)方法,简直是把你的榜样,使之穿过。

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

(这是由手工编码,是未经检验的,可能有一个bug,五 - 这是一个坏主意,反正)

这里的问题是,它会让你的机器笨重的,因为它必须一次分配一堆新线程。

所以修复起步价一旦所有的踏板问题,使用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(); 
    } 
} 

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

2

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.

3

我会更关心抛出异常的观察者而不是无限循环。您目前的实施将不会在此类事件中通知余下的观察员。