`
jy03100000
  • 浏览: 34197 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

设计模式:单例模式

 
阅读更多

定义

单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例

Singleton Pattern: Ensure a class has only one instance, and provide a global point of access to it

 

优缺点

优点:减少内存开支;减少系统性能开销;避免对系统资源的多重占用;优化和共享资源访问

缺点:不易扩展(?);不利于测试,为完成的单例模式不能使用mock虚拟;单例模式与单一职业原则有冲突

 

使用场景

  1. 生成唯一序列号
  2. 在整个项目中需要一个共享访问点或共享数据,如web页面上的计数器
  3. 创建一个对象需要消耗的资源过多,访问IO和数据库

PS:一个典型应用,spring中,每个Bean默认都是单例的)

 

实现方法

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在

 

具体实现

饿汉式:无论是否使用均实例化对象

 

public class SingletonEager {
 
    private static final SingletonEager instance = new SingletonEager();
 
    public static SingletonEager getInstance() {
        return instance;
    }
 
    private SingletonEager() {
    }
}

  

懒汉式(最常用):在需要用到的时候才进行第一次初始化

 

public class SingletonLazy {
 
    private static SingletonLazy instance = null;
 
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
 
    private SingletonLazy() {
    }
}

  

 

上述懒汉式存在一个问题,同步问题,多个线程同时访问该方法时,可能会实例化出多个对象,所以:

懒汉式*1 getInstance方法增加synchronized关键字

 

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

 

 

承上,synchronized方法会大大拖慢代码性能,因此:

懒汉式*2(这种形式也叫双重锁形式):将synchronized关键字放到if之后

 

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

  

 

小结:以上就是单例模式的几种基本写法,具体使用哪一种,就看具体使用环境的要求严格程度了。

  1. 如果确定该类会被经常调用到,那么直接写成饿汉式即可
  2. 如果该类不常被调用,对线程要求不严格,一般使用普通懒汉式即可
  3.  如果确定需要使用懒汉式,且对线程要求严格,那么推荐使用懒汉式*2。但这里特别说明一下,懒汉式*2依然可能存在同步问题,但那就已经涉及到JVM分配内存的机制问题了,此处不做那么深入的分析,如有问题,各位可参考本文最后的参考链接2

扩展模式:固定数量的单例

 

public class SingletonMulti {
 
    //最大实例数量
    private static int maxNumOfSingleton = 2;
    //实例名称列表
    private static ArrayList<String> nameList = new ArrayList<String>();
    //实例list
    private static ArrayList<SingletonMulti> singletons = new ArrayList<SingletonMulti>();
    private static int countNumOfSingleton = 0;
 
    static {
        for (int i = 0; i < maxNumOfSingleton; i++) {
            singletons.add(new SingletonMulti("Sample " + i));
        }
    }
 
    private SingletonMulti() {
    }
 
    private SingletonMulti(String name) {
        nameList.add(name);
    }
 
    //随机获取一个单例对象
    public static SingletonMulti getInstance() {
        Random random = new Random();
        countNumOfSingleton = random.nextInt(maxNumOfSingleton);
        return singletons.get(countNumOfSingleton);
    }
}

 

 

 补充:最后还有一种叫做登记式单例模式,这个模式似乎在spring里用于管理单例,本人不是特别理解,在此不做详述,以后有机会看了spring的源码可能会对此做进一步探讨。有兴趣的可以看一下参考内容中列到的第3个链接单例模式详解

 

单例模式vs静态方法

    关于这个问题,国内外都有各种讨论,对于内存分配的一些细节问题,在下实在才疏学浅,搞不清楚java底层的内存分配规则,只能结合我看到的内容,谈一下自己对用法上一些直观的看法:

  1. 如果你的类不需要维持任何状态, 仅仅是提供全局的访问 , 这个时候就适合用静态方法 , 这样速度也更快。反之,则使用单例
  2. 单例支持延迟加载 , 而静态方法则不支持这样的特性 , 对于重量级的对象, 延迟加载就很重要
  3. 单例对于静态方法的另一个优点是更加面向对象,对于单例你可以使用继承和多态

 

参考内容:

  1. 《设计模式之禅》秦小波
  2. http://devbean.blog.51cto.com/448512/203501/ 深入java单例模式
  3. http://www.cnblogs.com/whgw/archive/2011/10/05/2199535.html java单例模式详解
  4. http://blog.csdn.net/johnny901114/article/details/11969015 单例模式vs静态方法

 

 

0
0
分享到:
评论
1 楼 alanzyy 2015-05-11  
赞一个,连载起来

相关推荐

Global site tag (gtag.js) - Google Analytics