码迷,mamicode.com
首页 > 其他好文 > 详细

【设计模式】单例模式-孤独是一个人的狂欢

时间:2017-08-30 23:39:34      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:代码块   可见   同步代码块   很多   vol   对象   多个实例   访问   sync   

单例模式

使用单例模式,可以确保在整个系统中仅有该类的一个实例。此外,单例模式的类会提供一个访问类实例的全局访问点。相比使用关键字 new来直接实例化一个对象 ,使用单例模式,将对象的创建"委托"给了类的一个静态方法。在该静态方法中实现对象的实例化并将其返回,同时该方法的执行过程也提供了控制实例化对象的时机。

 

1. 定义

  该模式确保一个类只有一个实例,并提供一个全局访问点。

 

2. 为什么需要

  对于一些类来说,只有一个实例是很重要的,且有些类只应该存在一个实例。比如,线程池、缓存、日志对象等。

  对于一些可以共享,并且非常耗费资源的对象,如果实例化多个对象,很显然会加重系统的负担。此外,它可能也不会带来收益,更甚可能引发一些问题。而对于那些在业务领域本应该只存在一个实例的领域对象,使用单例模式就更是理所当然了。

  就好比,国家规定了“一夫一妻”制度,如果你非得 new Wife(); 两次,那必然你的系统就会出问题。如果,在一个“国家”的作用域范围内,实例化了两个"主席"对象,那也会导致系统出现问题。

 

3. 实现方案

  虽然,单例模式实现的功效较为简单,但是在不同的场景下也提供了不一样的实现机制。

3.1 “延迟”式单例

  它的实例化对象只有在真正需要的时候,才会被创建。代码如下:

public class LazySingleton {
    
    private static LazySingleton singleton; //使用静态对象来存储唯一实例
    
    /*
        该构造器的访问描述符为 private,所以使得其他的类不可以通过构造器来直接实例化该对象。
        那么怎么来构造该类的对象呢?
        通过下面提供的静态方法,在其中通过调用这个私有的构造器来实例化对象。与此同时,在该方法中控制使得该类只能有一个实例化对象。
        当然,单例的构造器也是可以传入参数的。只需要在下面的实例化方法getInstance()中传入构造对象需要的参数,即可。
     */
    private LazySingleton() {
        
    }
    
    /*
        该方法实现了对象的构造,并控制使得只能构造一个实例化对象。
     */
    public static LazySingleton getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton();
        }
        
        return singleton;
    }
}

  想要获得该单例对象,只需要使用静态方法 getInstance() 即可。该方法在多线程场景下不能保证只创建一个实例对象。如果存在两个线程A,B,且都需要获取该单例对象,则存在如下可能:

  1. 系统初始 singleton = null; 线程A和B分别调用 LazySingleton.getInstance(); 方法。
  2. 线程A执行 if 判断语句,条件为 true ,进入 if 代码块。且线程调度切换到线程B执行。
  3. 线程B执行 if 判断语句,此时 singleton 依然为 null ,因此进入 if 语句块,并实例化了一个单例对象,并返回。
  4. 切换到线程A,线程A直接调用 new 构造另一个单例对象,并返回。
  5. 此时,A、B线程返回不同的实例对象。

3.2 “饥饿”式单例

  对于某些对象,如果在创建和运行时的负担不太重,你想要急切的创建此单例,那么可以使用该方式来实现单例。代码如下:

public class HungrySingleton {
    private static HungrySingleton singleton = new HungrySingleton(); // 直接创建唯一实例
    
    private HungrySingleton() {
        
    }
    
    /*
        获取对象的唯一访问点
     */
    public static HungrySingleton getInstance() {
        return singleton; // 每次直接使用已创建的实例对象返回
    }
}

  该方法在静态初始化中创建单例对象,可以保证线程安全

3.3 “双重检查加锁”式单例

  该方法可以避免在多线程场景下,在系统中出现单例类的多个实例的情况。代码如下:

public class DoubleCheckSingleton {

    private volatile static DoubleCheckSingleton singleton; // volatile 修饰符保证变量在多线程之间的可见性

    private DoubleCheckSingleton() {}

    public static DoubleCheckSingleton getInstance() {
        if (singleton == null) {  // 检查实例,如果不存在就进入同步区
            synchronized (DoubleCheckSingleton.class) {
                if (singleton == null) { // 进入同步区后在检查一次,如果还是为null,才创建对象
                    singleton = new DoubleCheckSingleton();
                }
            }
        }
        return singleton;
    }
}

  这种方法,可以“最小化”同步代码块,可以减少时间的耗费,提高性能。

 

4. 总结

  单例模式可能是所有设计模式中最简单的一种了,但是它却是实际应用中很有可能碰到的一种设计模式。它简单,也简约,但威力不小。它存在如下优点:

  1. 对唯一实例的受控访问
  2. 缩小命名空间,该模式是对全局变量的一种改进,避免了使用那些存储唯一实例的全局变量来污染命名空间。
  3. 允许对操作和表示的精化。
  4. 允许可变数目的实例。事实上,可以在 getInstance() 方法中控制实例化多个对象。对象数为1,可以视为一种特例。

 

  孤独是一个人的狂欢。在整个系统中,单例对象都只存在一个,相比存在很多同胞的 Person, Student 等类对象它是孤独的,但这也突显了它在系统的重要地位。它就好比“濒危物种”。与此同时,所谓能力越大责任越大,因为没有同伴来分担压力,所以所有与该类的"交流"都需要该单例对象来完成,这也证明了它的“能力强大”。

  对于程序员而言,想要保持竞争力,事实上就是一个向着“单例”对象发展的过程。在团队里,如果你在某方面是独一无二的“对象”,那么你自然就无可取代。

  强行喂自己一口鸡汤:只有先经历一个人的孤独磨炼,才能享受和别人一起狂欢。

【设计模式】单例模式-孤独是一个人的狂欢

标签:代码块   可见   同步代码块   很多   vol   对象   多个实例   访问   sync   

原文地址:http://www.cnblogs.com/liujiong/p/7450736.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!