毕业那年找工作,在学校面试的时候,有一个题目是让我写个单例,当时心想这玩意不是手到擒来分分钟写了2个,不知不觉工作了2年再回头看这个最简单的单例模式,还是有很多东西的,网上多数讲解并没有讲清楚。
1.饿汉单例
最简单的单例,私有化构造方法,提供一个get方法,内部搞一个实例。 我是线程安全的,但是我没有实现懒加载!
public class Singleton {
private static Singleton INSTANCE = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return INSTANCE;
}
}
2.懒汉单例
第一种单例不管这个实例是否用到都初始化了一个,显然没必要,用到的时候再初始化。 我也是线程不安全的哦!
public class Singleton {
public static Singleton getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
3.加锁单例
以上两种单例都是线程不安全的,想要线程安全,加个锁搞定啦; 我是线程安全的哦
public class Singleton {
private static Singleton INSTANCE;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
4.双层锁检测单例
对于第三种写法的单例,其实只要初始化的时候加上锁就可以了,但是直接加到方法上就会导致每次访问单例都是带锁的,于是就有了双层锁检测单例。
我是线程安全的哦
这种单例双层锁不少人知道的,但是 volatile 这个关键字没有加上,那就没有意义了,初始化时分为实例化和赋值两步,但是Java虚拟机不能保证在其他线程中这两步骤的顺序,加上 volatile 这个关键字就能保证实例化和赋值是有序的,简单说,在另一个线程眼中,只要这个INSTANCE为null初始化就没完成,只要完成了INSTANCE就不为null,我看到不少双层锁单例没加这个关键字,那就没意义了。
public class Singleton {
private static volatile Singleton INSTANCE;
private Singleton(){}
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized (Singleton.class){
if(INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
5.静态内部类单例
我是线程安全的哦
其实我们的单例就想保证线程安全和懒加载,如下这种静态内部类写法我解释一下: 首先加载 Singleton 的时候并没加载Holder,懒加载已经实现了。 然后对于线程安全,在第一次掉 getInstance 的时候虚拟机发现 Holder 没有初始化,那就初始化Holder,而虚拟机会保证类加载正常完成所以类加载是线程安全的,相当于把线程安全的事情由虚拟机保证了。
public class Singleton {
private static class Holder{
private static Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return Holder.INSTANCE;
}
}
以上是对单例模式的总结。