什么是单例模式?
在我们生活中,有很多对象是只能存在的一个的,比如,世界上只有一个太阳,一个国家只能有一个正的主席(总统),一个公司只有一个正的董事长、CEO。这些都是单例模式在我们生活中的体现。单例模式让我们的这个对从头到尾都是同一个,保证了属性不变,这样可以节约资源,不用花费多余的空间去新建对象。
单例模式的特性
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式的实现形式有很多种,下面我们一个一个的介绍!
懒汉式,线程不安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式,线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
对比上面的两个单例模式的写法,后面一个相对于前面加了一个同步,保证了在同一时刻,只有一个线程能够访问这个程序,保证了线程的安全。但是上面这个虽然保证了线程的安全,解决了多个实例的问题,但是它的效率并不高。因为在任何时候只能有一个线程调用getInstance()方法。但是同步只需要第一次调用时才被需要,即第一次创建单例对象时。这就引出了双重检验锁。
双重检验锁,线程安全
public class Singleton {
private volatile static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) { //Single Checked
synchronized (Singleton.class){
if(instance == null){ //Double Checked
instance = new Singleton();
}
}
}
return instance;
}
}
双重锁保证了当多个线程进入同步块外的if,经过二次检测,保证最后只有一个线程进入内部。第一句加的volatile保证各原子性,因为在执行 instance= new Singleton()这句时,JVM做了一下三件事:
1、给instance分配内存;
2、调用Singleton的构造函数来初始化成员变量;
3、将instance对象指向分配的内存空间(执行完这一步instance就不是NULL了)
但是在JVM即时编译器中存在指令重排序的优化。也就是不能保证一定按123顺序来执行,造成程序的错误,所以加上关键词volatile来保证原子性。
饿汉式
public class Singleton{
//类加载时就初始化
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
这种方法简单粗暴,犹如名字,在刚开始的时候我就把你初始化,即使你还没有调用getInstance()方法;这样导致它在某些场合是无法使用的,比如说创建实例需要依赖参数或者配置文件,那就无法使用了。
静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种写法仍然使用JVM本身机制保证了线程安全问题;由于SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
这种方法我第一眼看到就喜欢上了,面试的时候,写出这玩意儿太容易了!它自动支持序列化机制,绝对防止多次实例化。
参考博客:
http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/
http://www.runoob.com/design-pattern/singleton-pattern.html