【大话设计模式】模式一 :单例模式与多线程

一、基本介绍

单例模式(Singleton),保证一个类仅仅有一个实例,并且提供一个访问它的全局访问点。

        通常我们可以让一个全局变量使其被一个对象访问,但是它不能防止实例化多个对象。最好的方法就是,让类自身负责保存它的唯一实例,同时这个类保证没有其他实例可以被创建,并且它提供了一个访问该实例的方法。​​​

模式构成要素:

  • 私有构造方法
  • 私有静态引用指向自己的实例
  • 以自己实例为返回值的公有静态方法

二、模式应用场景及举例

1、适用场景:
1.需要生成唯一序列的环境。
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
4.方便资源相互通信的环境。

2、场景举例

以下是单例模式的经典使用场景: 

1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。 

其他应用场景举例: 
    1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 
    2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 
    3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 
    4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 
    5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 
    6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 
    7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 
    8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 
    9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 
    10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例. 

三、实现方式以及优缺点

【大话设计模式】模式一 :单例模式与多线程_第1张图片

 1.饿汉式:单例实例在类装载时就构建,急切初始化。(预先加载法) 

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

优点 :

  • 它基于 classloder 机制避免了多线程的同步问题,线程安全 
  • 在类加载的同时已经创建好一个静态对象,调用时反应速度快 

缺点: 

  • 类加载时就初始化,浪费内存。
  • 资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化

 另外一种实现,静态内部类

public class Singleton {
        private Singleton() {
        }

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

        public static Singleton getInstance() {
            return SingletonBuilder.instance;
        }
    }
/*
    优点:
    资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法
    缺点:
    第一次加载时反应不够快*/

2.懒汉式:单例实例在第一次被使用时构建,延迟初始化。

public class Singleton {
    private static Singleton instance;
    //构造器私有化
    private Singleton() {
    }
    /**
     * 若实例存在返回实例,否则创建实例
     * @return instance实例
     */
    public static Singleton getInstance(){

        if(instance ==null){
            instance =new Singleton();
        }
        return instance;
    }
}

优点: 

        避免了饿汉式的那种在没有用到的情况下创建事例,资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法。 

缺点: 

         懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同事创建多个实例,而且这多个实例不是同一个对象,虽然后面创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。解决这个问题的办法就是加锁synchonized,第一次加载时不够快,多线程使用不必要的同步开销大。 


3、多线程时的单例模式

public class Singleton {
    private static Singleton instance;
    //构造器私有化
    private Singleton() {
    }
    public static Singleton getInstance() {
        //临界区互斥访问
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        return instance;
    }
}

        这样有个很大的问题,每次调用GetInstance方法都需要lock,很大地影响性能,因此需要改良,所以有了双重锁定(Double-Check-Lock)。

4、双重锁定(Double-Check-Lock)

public class Singleton {
    private static Singleton instance;
    //构造器私有化
    private Singleton() {
    }
    public static Singleton getInstance() {
        //临界区互斥访问
        //先判断实例是否存在,不存在再进行加锁处理
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    //检查该单例是否已经被创建,保证单例模式
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点 :资源利用率高,不执行getInstance()就不被实例,可以执行该类其他静态方法 。
缺点 :第一次加载时反应不快,由于java内存模型一些原因偶尔失败 。

四、JDK中单例模式

1、java.awt.Toolkit

        ToolKit 是一个抽象类,ToolKit 作为 AWT 工具箱,提供了 GUI 最底层的 Java 访问,例如从系统获取图像、获取屏幕分辨率,获取屏幕色彩模型、全屏的时候获得屏幕大小等。很多时候并不常用,适合使用懒汉模式,使用饿汉反而会导致虚拟机JVM启动过慢。

【大话设计模式】模式一 :单例模式与多线程_第2张图片

 2、Runtime类是使用的单例设计模式。

        采用饿汉模式,提高虚拟机加载速度。

【大话设计模式】模式一 :单例模式与多线程_第3张图片

 五、总结

总结: 
    一般采用饿汉式,若对资源十分在意可以采用静态内部类,不建议采用懒汉式及双重检测

你可能感兴趣的:(设计模式,单例模式,开发语言,java)