您的位置:首页 > 其它

浅谈设计模式

2017-04-25 21:21 337 查看

存在目的

节约资源确保系统中某个类只有一个实例。(确保对象的唯一性,避免不必要的系统开销)

单例模式的三个要点



由三个要点可知单例模式的结构特点:

内部实现只生成一个实例

提供静态的 getInstance() 工厂方法,让客户可以访问它的唯一实例

构造函数设为私有,在单例类内部定义一个静态 Singleton 类型对象,作为外界使用的唯一实例

基本 UML 图:



单例模式实现方式

饿汉式

public class EagerSingleton {

private final static EagerSingleton instance = new EagerSingleton();

private EagerSingleton() {
}

public static EagerSingleton getInstance() {
return instance;
}
}


类加载时静态变量 instance 会被初始化,此时私有构造方法被调用,单例唯一实例被创建,可确保实例的唯一性,但是类加载时需要创建实例,可能会导致加载时间变长。

懒汉式

public class LazySingleton {

private  static LazySingleton instance = null;

private LazySingleton() {
}

public static LazySingleton getInstance() {
if (instance == null)
instance = new LazySingleton();
return instance;
}
}


这是最简单的懒汉式,只有用到的时候才去判断有没有实例,但是这样 getInstance() 并不能在多线程时保证只创建出一个实例,可以修改为:

public static LazySingleton getInstance() {
if (instance == null)
synchronized (LazySingleton.class) {
instance = new LazySingleton();
}
return instance;
}


因为 判空和 new 对象的操作并不是原子操作,所以依然不能保证原自性,也就也可能,A、B 线程先后访问 instance 为 null,A 在 new 对象,而 B 在等锁,然后 B 又去 new 了一个实例。同样不能保证原子性。

把整个 getInstance() 方法加锁来保证原子性的话,作为一个单例被各种其他类访问的第一步都是在 getInstance(),这样会在多线程的时候影响执行效率,所以又有了下面这种:

public class LazySingleton {

private static volatile LazySingleton instance = null;

private LazySingleton() {
}

public static LazySingleton getInstance() {
if (instance == null)
synchronized (LazySingleton.class) {
if (instance == null)
instance = new LazySingleton();
}
return instance;
}
}


这样貌似就完美的解决了执行效率和实例唯一性的问题。但是需要注意到的一点是:必须在 instance 前面加修饰符 volatile 来确保多线程正确处理。

volatile 只有在 JDK 1.5 以及以上的版本才能正确执行。volatile 会屏蔽一些 Java 虚拟机的优化,而影响执行效率。可见上面这种双重判断锁定也不是一种完美的单例解决方案。

静态内部类实现单例(完美方案)

public class Singleton {
private Singleton() {
}

public static Singleton getInstance() {

return HolderClass.instance;
}

private static class HolderClass{
private final static Singleton instance = new Singleton();
}
}


这种方式实现了延时加载,保证了线程的安全性,又不影响系统性能。

单例模式优缺点

优点

单例模式提供了对唯一实例的受控访问,可以严格控制客户端怎样以及何时访问它

由于系统内只存在一个对象,比较节约系统资源。对于创建开销比较大,或者频繁创建开销的使用单例可以明显的提升性能。

缺点

单例模式没有抽象层,不利于拓展

违反了单一职责的原则,单例类既充当工厂,又充当产品的角色。将产品的创建和产品本身的功能融合到了一起。

长时间不被利用时,可能被当作垃圾回收掉。下次还要初始化实例。可能会引起必要的实例状态的丢失。

单例模式适用场景

系统只需要一个实例,或者是创建的开销太大,只允许创建一个实例

客户端使用实例只允许通过一个公共访问点,除了公共访问点以外不允许其他的途径来访问实例。

PS:

单例模式也可以根据需要进行改造,改造成两例,三例等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息