我看的设计模式书是《Head First设计模式》,我决定不按照书上的章节顺序作笔记,按照我认为的容易理解程度从易到难来写。设计模式
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。缓存
有一些对象其实咱们只须要一个,例如:线程池(threadpool)、缓存(cache)、对话框、处理器偏好设置和注册表(registry)的对象、日志对象等。多线程
咱们能够用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.1中咱们说过在getInstance方法加入synchronized关键字来解决多线程会实例化多个对象的问题,它也存在一些问题:设计
1)同步会下降性能;日志
2)更严重的是:上述代码只要第一次执行getInstance方法时,才真正须要同步。换句话说,一旦设置好uniqueInstance变量,就再也不须要同步这个方法了。以后每次调用这个方法,同步都是一种累赘。code
下面给出解决三种解决方案:对象
没错,就是这么直接!固然,若是getInstance()在程序中频繁的运行,那就得从新设计了。
若是程序老是会建立并使用到这个单例类,或者建立这个单例类的实例不繁重,能够急切的建立此单例:
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关键字的文章。
一、据说两个类加载器可能会各自建立本身的单例对象?
答:是的。每一个类加载器都定义了一个命名空间,若是有两个以上的类加载器,不一样的类加载器可能会同时加载同一个类,从整个程序来看,同一个类会被加载屡次。若是这样的事情发生在单件上,就会产生多个单例并存的现象。因此,若是程序有个多个类加载器又同时使用了单例模式,能够这样解决:自行指定类加载器,并指定同一个类加载器。
二、全局变量比单例模式差在哪里?
答:1)不可延迟实例化;2)并不能确保只有一个实例,并且也变相鼓励开发人员,用许多全局变量指向许多小对象来形成命名空间的污染。