双重锁单例模式 观察者模式

来源:互联网 发布:javascript实战.pdf 编辑:程序博客网 时间:2024/05/19 02:19
 

单例模式

标签: 设计模式-java
 162人阅读 评论(0) 收藏 举报
 分类:
 

在软件开发过程中常会有一些对象我们只需要一个,如:线程池(threadpool)、缓存(cache)、对话框、偏好设置等。这些对象如果制造出多个实例的话可能会导致一些不必要的麻烦,如:程序行为异常、资源使用过量等。这时单例模式就可以确保一个类只有一个实例,并提供全局访问点。下面是从简单的单例类来探讨该用何种方法实现单例模式。

/** * 最经典的单例类 */public class Singleton {    // 设置成静态变量来记录Singleton的唯一实例    private static Singleton singleInstance;    private Singleton(){        // 构造方法声明为私有的,这样只能在Singleton类中才能调用此构造方法    }    /*     * 获取Singleton对象,如果还未实例化则实例化一个对象并返回这个实例     */    public static Singleton getInstance(){        if (singleInstance == null) {            singleInstance = new Singleton();        }        return singleInstance;    }    // 其他方法}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

从上面的例子可以看出Singleton类自己管理这个类的实例化过程,而且提供了全局访问点,就是设置成静态的getInstance()方法,在其他类要使用Singleton时它会返回一个实例。这中单例模式有个优点就是延迟实例化,简单地说延迟实例化就是延迟初始化,在类需要时才创建其实例,而不是在开始加载这个类时就创建出一个实例,这样的好处是可以避免性能的浪费。例如有些对象无需程序一开始就使用,或者其在程序执行的过程中就没有使用过。但是此例子却又一个缺点,那就是线程不够安全。因为如果有多个线程同时执行到getInstance()方法,而Singleton又还未new Singleton()一个实例,那么线程就会都认为singleInstance为null,就都会实例化Singleton,这时就会产生多个Singleton实例,明显不符合单例模式的初衷。那么接下来可能要做的就是对其进行改进

public class SingletonA {    private static SingletonA singletongA;    private SingletonA(){    }    /*     * 增加synchronized关键字把getSingletonA方法变为同步方法     */    public static synchronized SingletonA getInstanceA(){        if (singletongA == null) {            singletongA = new SingletonA();        }        return singletongA;    }    // 其他方法}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从这个例子上看增加了synchronized可以使getInstanceA()变成一个同步的方法,这时线程在进入这个方法之前就需要等待其他线程离开这个方法才能进入,也就使得该方法只能同时存在一个线程在执行它。 
可能差不多问题解决了,但是要知道同步方法是会影响程序执行效率的,在此例子中我们只是为了解决第一个例子中第一次执行getInstance()方法不会产生多个实例,而这个例子中却会导致每次需要实例时都会调用getInstanceA()同步方法,而在已经有实例之后的调用synchronized就会是累赘,因为我们已经无需担心这个单例类会再次被创建出新的实例。因此我们还需要做一下改进。 
既然上面说到延迟实例化,那么如果是不用的话那就简单多了。

public class SingletonB {    // 在静态初始化器(static initializen)中创建单例,保证线程安全    private static SingletonB singletonB = new SingletonB();    private SingletonB(){        // 构造函数    }    public static SingletonB getInstaceB(){        // 已经实例化了,直接使用它        return singletonB;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

上面的这种做法是在JVM加载这个类时马上创建一个实例,因为JVM会在线程访问这个实例之前就创建出该实例,因此线程是安全的。但这相较于延迟实例化而言可能会出现资源的浪费。而且如果此类较大的情况下会时程序初始化时间加长。 
那么是否可以在使用用延迟实例化的同时又不会造成线程不安全且增加访问效率呢。接下来就用双重检查加锁来改进一下。

/** *  双重锁单例模式 */public class SingletonC {    private volatile static SingletonC singletonC;    private SingletonC(){    }    public static SingletonC getInstanceC(){        if (singletonC == null) {            synchronized (SingletonC.class) {                if (singletonC == null) {                    singletonC = new SingletonC();                }            }        }        return singletonC;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上面的例子是先检查实例,如果不存在则进入同步区块,进入同步区块之后再次检查,如果还是null才会创建实例,因而singletonC = new SingletonC()只会执行一次,而之后调用getInstanceC()时就因为有实例直接返回,所以除了第一次调用时会走同步,而之后便不会如第二个例子那样每次都会走同步方法。这样就可以使得执行getInstanceC()的时间减少。想必这里会发现有个volatile关键字,其作用是使得singletonC被初始化后对所有线程可见,多个线程可以正确地处理这个SingletonC变量。但要注意的:volatile关键字只能在Java 5及其之后使用,如果在此版本之前会导致这个双重检查失效。

在使用单例模式时,如果有多个类加载器(classloader)时需要自行指定类加载器,并指定用一个类加载器。因为每个类加载器都定义了一个命名空间,不同的类加载器可能会加载同一个类,从而导致单例类创建出多个实例。

-----------------------------------------------------------------------------------------------------------------------

 

观察者模式简单例子

标签: 设计模式-java
 1982人阅读 评论(0) 收藏 举报
 分类:
 

观测者模式定义了对象之间的一对多依赖,当一个对象状态发生改变时,其依赖者便会收到通知并做相应的更新。其原则是:为交互对象之间松耦合。以松耦合方式在一系列对象之间沟通状态,我们可以独立复用主题(Subject)/可观测者(Observable)和观测者(Observer),即只要遵守接口规则改变其中一方并不会影响到另一方。这也是其主要的设计原则。下面是一个简单的气象站发送天气信息给布告板,然后布告板把天气信息显示在板上的例子。 
首先先建立三个接口,主题(Subject)、观测者(Observer)和显示内容(DisplayElement),分别代表气象站、布告板信息接收和布告板信息显示。

/** *  主题 */public interface Subject {    // 观察者注册    public void registerObserver(Observer o);    // 删除观察者    public void removeObserver(Observer o);    // 当主题有内容更新时调用,用于通知观察者    public void notifyObserver();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
/** *  观察者 */public interface Observer {    // 当气象站观测的天气发生变化时,主题会把参数值传给观察者    public void update(float temp);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
/** *  用于布告板显示 */public interface DisplayElement {    // 在显示布告板上显示的操作    public void display();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

接下来是实现气象站()和布告板()了。

/** * 气象站实现主题,发布气象信息(气温) */public class WeatherStation implements Subject{    private ArrayList observers;    private float temp;    public WeatherStation() {        // 加个ArrayList存放所有注册的Observer对象;        observers = new ArrayList<>();    }    @Override    public void registerObserver(Observer o) {        // 当新的观察者注册时添加进来        observers.add(o);    }    @Override    public void removeObserver(Observer o) {        // 当观察者取消注册时去除该观察者        int i = observers.indexOf(o);        if (i>=0) {            observers.remove(i);        }    }    @Override    public void notifyObserver() {        // 更新状态,调用Observer的update告诉观察者有新的信息        for (int i = 0; i < observers.size(); i++) {            Observer observer = (Observer) observers.get(i);            observer.update(temp);        }    }    /*     *  此方法用于气象站收到的数据,并且调用更新使数据实时通知给观察者     */    public void setMeasurements(float temp){        this.temp = temp;        System.out.println("气象站测量的温度为:" + temp + "℃");        notifyObserver();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
/** * 布告板上的状态显示 */public class ConditionDisplay implements Observer,DisplayElement{    private float temp;    private Subject weatherStation;    public ConditionDisplay(Subject weatherStation) {        // 构造时需要间主题/被观察者对象作为注册之用        this.weatherStation = weatherStation;        weatherStation.registerObserver(this);    }    @Override    public void display() {        // 将数据显示在布告板上        System.out.println("布告板显示当前温度为:" + temp + "℃");    }    @Override    public void update(float temp) {        // 接受来自主题/被观察者(气象站)的数据        this.temp = temp;        display();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

测试结果

/** * 天气观测站 */public class WeatherObserver {    public static void main(String[] args) {        // 首先创建一个主题/被观察者        WeatherStation weatherStation = new WeatherStation();        // 创建观察者并将被观察者对象传入        ConditionDisplay conditionDisplay = new ConditionDisplay(weatherStation);        // 设置气象站模拟收到的气温数据        weatherStation.setMeasurements(25);        weatherStation.setMeasurements(24);        weatherStation.setMeasurements(23);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这里写图片描述




原创粉丝点击