最简单的设计模式--单例模式

  我看的设计模式书是《Head First设计模式》,我决定不按照书上的章节顺序作笔记,按照我认为的容易理解程度从易到难来写。设计模式

  1、单例模式的定义

  单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。缓存

  2、实例说明

  有一些对象其实咱们只须要一个,例如:线程池(threadpool)、缓存(cache)、对话框、处理器偏好设置和注册表(registry)的对象、日志对象等。多线程

  2.1 一个最简单的经典单例模式代码

  咱们能够用Java的静态变量来坐到这一点,但事静态全局变量在程序一开始就被建立好对象,若是这个对象很是耗费资源,而程序在此次执行过程当中又一次没用到它,就造成浪费。性能

经典的单例模式代码:spa

 1 public class Singleton {
 2     private static Singleton uniqueInstance;
 3     private Singleton(){}//构造器声明为私有,只有类内才能够调用构造器
 4     public static Singleton getInstance(){
 5         if(uniqueInstance==null){
 6             uniqueInstance=new Singleton();
 7         }
 8         return uniqueInstance;
 9     }
10 }

 

  别的类用要用这个类的对象的话,就经过Singleton.getInstance()来获取,if判断并且确保了这个类只有一个实例化的对象。这样彷佛都一切正常了。不过实际项目中,确定会有多线程的场景,那样就可能产出两个实例。例如这里有两个线程,线程1运行到上述代码第5行时候,new 一个对象,假如此刻线程2也进入5行,发现此时uniqueInstance为null,它也new 一个对象,那么就产生了两个实例化对象了。因此,要在第4行上面加上synchronized,这样就保证了不会存在两个线程同时进入到getInstance方法。线程

  2.2改善多线程

  在2.1中咱们说过在getInstance方法加入synchronized关键字来解决多线程会实例化多个对象的问题,它也存在一些问题:设计

  1)同步会下降性能;日志

  2)更严重的是:上述代码只要第一次执行getInstance方法时,才真正须要同步。换句话说,一旦设置好uniqueInstance变量,就再也不须要同步这个方法了。以后每次调用这个方法,同步都是一种累赘。code

  下面给出解决三种解决方案:对象

  2.2.1 若是getInstance()的性能对应用程序不是很关键,就什么都别作

   没错,就是这么直接!固然,若是getInstance()在程序中频繁的运行,那就得从新设计了。

  2.2.2 使用“急切”建立实例,而不用延迟实例化的作法

  若是程序老是会建立并使用到这个单例类,或者建立这个单例类的实例不繁重,能够急切的建立此单例:

public class Singleton {
    private static Singleton uniqueInstance=new Singleton();//在静态初始化中建立单例
    private Singleton(){}
    public static Singleton getInstance(){
        return uniqueInstance;
    }
}

  2.2.3 用“双重检查加锁”,在getInstance()中减小使用同步

  首先检查是否实例已经建立了,若是没有,“才”进行同步。这样一来,只有第一次建立会用到同步,这正是咱们想要的。

 1 public class Singleton {
 2     private volatile static Singleton uniqueInstance;
 3     private Singleton() {}
 4     public static Singleton getInstance() {
 5         if (uniqueInstance == null) {
 6             synchronized (Singleton.class){
 7                 if(uniqueInstance == null){//进入区域后,再检查一次,若是还是null,才建立实例
 8                     uniqueInstance = new Singleton();
 9                 }
10             }
11         }
12         return uniqueInstance;
13     }
14 }

  第7行还须要加入一次判断,有可能别的线程在此线程拿5-6行的期间,已经实例化了。(ps:个人我的理解)关于volatile关键字,不清楚的同窗能够去查,之后等我看完JVM,会专门写一篇volatile关键字的文章。

  3、单例模式的延伸

  一、据说两个类加载器可能会各自建立本身的单例对象?

  答:是的。每一个类加载器都定义了一个命名空间,若是有两个以上的类加载器,不一样的类加载器可能会同时加载同一个类,从整个程序来看,同一个类会被加载屡次。若是这样的事情发生在单件上,就会产生多个单例并存的现象。因此,若是程序有个多个类加载器又同时使用了单例模式,能够这样解决:自行指定类加载器,并指定同一个类加载器。

  二、全局变量比单例模式差在哪里?

  答:1)不可延迟实例化;2)并不能确保只有一个实例,并且也变相鼓励开发人员,用许多全局变量指向许多小对象来形成命名空间的污染。

相关文章
相关标签/搜索