说到单例模式,大家都很熟悉,这好像是最简单的一个最简单也是容易被问的一个设计模式。以下是几种实现单例模式的方式,一个单例模式肯定有一个私有的构造方法。
(1)饿汉式
public class SingletonObject1 {
private SingletonObject1(){
}
private static SingletonObject1 singletonInstance=new SingletonObject1();
public static SingletonObject1 getSingletonInstance(){
return singletonInstance;
}
}
饿汉式就如同它的名字,一开始就直接初始化,是线程安全的。但是如果这个实例不被使用而一开始就直接初始化,会浪费内存空间,没有懒加载。
(2)懒汉式
public class SingletonObject2 {
private SingletonObject2(){
}
private static SingletonObject2 singletonInstance;
public static SingletonObject2 getSingletonInstance(){
if(singletonInstance==null){
singletonInstance=new SingletonObject2();
}
return SingletonObject2.singletonInstance;
}
}
懒汉式在需要使用他的时候才进行加载,但是在多线程的情况下,可能会new出多个实例,是线程不安全的。
(3)懒汉式同步方法
public class SingletonObject3 {
private SingletonObject3(){
}
private static SingletonObject3 singletonInstance;
public synchronized static SingletonObject3 getSingletonInstance(){
if(singletonInstance==null){
singletonInstance=new SingletonObject3();
}
return SingletonObject3.singletonInstance;
}
}
因此,提出了在获取实例的方法加上synchronized关键字,但是同步方法是方法串行化,影响性能
(4)同步代码块双重验证
public class SingletonObject4 {
private SingletonObject4(){
}
private static SingletonObject4 singletonInstance;
public static SingletonObject4 getSingletonInstance(){
if(singletonInstance==null){
synchronized (SingletonObject4.class){
if(singletonInstance==null){
singletonInstance=new SingletonObject4();
}
}
}
return SingletonObject4.singletonInstance;
}
}
为了提高性能,采用了同步代码块加双重验证的方法,但是这种方式在多线程情况下,指令重排序可能会引起空指针异常
(5)加volatile关键字
public class SingletonObject5 {
private SingletonObject5(){
}
private volatile static SingletonObject5 singletonInstance;
public static SingletonObject5 getSingletonInstance(){
if(singletonInstance==null){
synchronized (SingletonObject5.class){
if(singletonInstance==null){
singletonInstance=new SingletonObject5();
}
}
}
return SingletonObject5.singletonInstance;
}
}
volatile关键字可以禁止指令重排序。JVM对指令重排序是基于对代码运行速度进行优化的原则,禁止指令重排序会降低性能
(6)静态内部类
public class SingletonObject6 {
private SingletonObject6(){
}
private static class single{
private static SingletonObject6 singletonInstance=new SingletonObject6();
}
public static SingletonObject6 getSingletonInstance(){
return single.singletonInstance;
}
}
外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化singletonInstance,故而不占内存。只有当getSingletonInstance()方法第一次被调用时,才会去初始化singletonInstance,第一次调用getSingletonInstance()方法会导致虚拟机加载single类,且仅加载这一次。这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
(7)枚举
public enum SingletonObject7 {
SINGLETONINSTANCE
}
枚举在java中与普通类一样,都能拥有字段与方法,而且枚举实例创建是线程安全的,在任何情况下,它都是一个单例。我们可直接以SingletonObject7 .SINGLETONINSTANCE的方式调用。