特点:
1.构造函数不外放,一般都是private
2.通过暴露一个public的静态方法或者枚举来返回一个单例对象
3.在多线程环境下面,确保单例类对象有且只有一个
4.确保单例类对象再反序列化时候不会重新构建对象
懒汉模式:
1 public class Singleton{ 2 private static Singleton instance; 3 //1.构造函数不对外开放 4 private Singleton{ 5 } 6 //2.通过一个静态方法来返回单例类对象 7 //3.加了synchronized,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized修饰的方法或者代码块。 8 //也就是getInstance是一个同步方法,保证多线程下对象唯一 9 public static synchronized Singleton getInstance(){ 10 if(instance == null){ 11 instance = new Singleton(); 12 } 13 return instance; 14 } 15 16 }
优点:需要时候才实例化,节约资源
缺点:每次调用getInstance()都要同步,造成不必要的同步开销
Double checkLock实现单例:
1 public class Singleton{ 2 private volatile static Singleton singleton; 3 private Singleton (){} 4 public static Singleton getInstance() { 5 //第一层判空是为了避免不必要的同步 6 //从而只有第一次的时候才会加锁,之后就不会加锁 7 if (singleton == null) { 8 synchronized (Singleton.class) { 9 if (singleton == null) { 10 //第二层判空是为了在null的情况下创建实例 11 singleton = new Singleton(); 12 } 13 } 14 } 15 return singleton; 16 } 17 }
注意DCL是有问题的:
singleton = new Singleton();
这句并不是原子操作,它可以拆分三件事:
(1).给Singleton的实例分配内存
(2).调用Singleton的构造函数,创建对象
(3).将singleton的对象指向内存分配的空间
但是,由于Java编译器允许处理器乱序执行,以及JDK1.5之前JVM中Cache,寄存器以及主内存回写顺序的规定,(2)与(3)的顺序不是固定的,
所以可能执行顺序是123,也可能是132
如果是132,那么此时比如A线程执行到13,还未执行2,此时B线程也在执行,检查singleton非空,直接使用,就会出错
所如果是JDK1.5以及之后的版本,加上Volatile关键字:
private volatile static Singleton singleton;
这样就可以保证singleton对象每次都是从主内存中读取。
静态内部类单例模式:
1 public class Singleton{ 2 private Singleton{} 3 public static Singleton getInstance(){ 4 return SingletonHolder.sInstance; 5 } 6 /* 7 静态内部类 8 */ 9 private static class SingletonHolder{ 10 private static final Singleton sInstance = new Singleton(); 11 } 12 }
优点:只有第一次调用getInstance才会导致sInstance被初始化,第一次调用时候会导致虚拟机加载SingletonHolder类,
这样能保证线程安全,也能保证单例对象唯一,还能延迟单例实例化
枚举单例:
1 public enum SingletonEnum{ 2 INSTANCE; 3 }
优点:线程安全,唯一,还能防止反序列化。
破坏单例对象的唯一:
1.反射
2.序列化与反序列化
http://www.hollischuang.com/archives/1144
原文:http://www.cnblogs.com/wufeng0927/p/5202466.html