一.什么是单例模式?
单例是指在程序中某个类只能有一个实例,一些管理器和控制器常被设计成单例模式(如Spring容器的bean实例默认都是单例模式存在的)
二.单例有什么好处?
(1)避免对象的重复创建,减小时间和内存开销
(2)避免由于操作多个对象导致逻辑错误,可以全局统一管理控制
三.怎样创建单例模式?
(1)饿汉模式
public class Singleton1 {
// 私有化构造函数,保证其它类不能实例化此类
private Singleton1(){}
private static Singleton1 instance = new Singleton1();
//给外界提供一个获取该实例的方法
public static Singleton1 getInstance() {
return instance;
}
}
饿汉模式是实例在类加载的时候创建
好处:避免线程同步问题
缺点:即使这个单例不会被用到也会创建,浪费内存资源
(2)懒汉模式
public class Singleton2 {
private Singleton2(){}
private static Singleton2 instance = null;
public static Singleton2 getInstance() {
if(instance == null){
return new Singleton2();
}
return instance;
}
}
懒汉模式是单例在需要的时候才会去创建
好处:按需创建,避免内存资源浪费
坏处:会有线程安全问题产生(需要加锁解决线程同步问题)
(3)双重校验锁
public class Singleton3 {
private Singleton3(){}
private static volatile Singleton3 instance = null;
public static Singleton3 getInstance() {
if(instance == null){
synchronized (Singleton3.class){
instance = new Singleton3();
}
}
return instance;
}
}
上面说到的懒汉模式加同步锁最先想到的可能是在getInstance外直接加synchronized,但仔细想想,每次来拿该对象都需要判断对象锁,这样无疑会降低代码执行效率,所以在同步前先判断对象是否为空,如果不为空直接将对象给它,不需要进行同步操作(这样层序运行过程中最多也只有一次同步锁的判断)你一定还发现对象声明的修饰符多了一个volatile,这就是第二把锁,它能起到禁止指令重排序优化的作用,这样就避免了对象内存地址被赋给instance字段但是构造函数还没调用。(具体原因详见—关于volatile的理解最后一段)
(4)静态内部类
public class Singleton4 {
private static class Singleton{
public static Singleton4 instance = new Singleton4();
}
private Singleton4(){}
public static Singleton4 newInstance(){
return Singleton.instance;
}
}
与饿汉模式一样同样是利用类加载机制来创建实例,因此不存在多线程并发问题,不同的是它是在内部类里去加载这个实例,所以只要不使用这个内部类,JVM就不会加载这个单例类,也就不会创建对象,实现了懒汉式的延迟加载。既能保证延迟加载,又能保证线程安全
(5)枚举
public enum Singleton5 {
instance;
public void whateverMethod(){}
}
上面的四种方案都有两个弊端:
1)需要额外的工作进行序列化,否则每次反序列化的结果都是一个新的实例
2)可通过反射机制强行调用私有构造器实现创建多个对象
而这种方案完美的解决了上述两种问题