目录
一、什么是单例模式
单例模式,就是使一个类只能产生一个实例。
应用场景:
- 当一个对象的产生需要比较多的资源时,则可以在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
- 应该只有一个实例的情形,如系统任务管理器、网站计数器等,也可以使用单例模式保证只产生一个实例。
常见的单例模式的实现方式有:
主要:
- 饿汉式(线程安全,调用效率高。 但不能延迟加载)
- 懒汉式(线程安全,但调用效率不高,可以延迟加载)
其他:
- 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)
- 静态内部类式(线程安全,调用效率高, 而且可以延迟加载)
- 枚举式 (线程安全,但调用效率不高,也不能延迟加载)
二、单例模式的实现
1. 饿汉式
饿汉式,指像一个饿汉一样,等不及了,管他三七二十一,一上来就创建实例。
写单例模式的两个要点:
(1)将构造器设为private, 这样在外部就不能调用了。
(2)提供一个public static的方法,使外部可以通过此方法获取到实例。
public class MySingleton {
private static MySingleton instance = new MySingleton(); //一上来就创建实例
private MySingleton(){} //私有化构造器
//public static方法提供获取途径
public static synchronized MySingleton getInstance(){
return instance;
}
}
2. 懒汉式
懒汉式,就是像一个懒汉一样,一动都懒得动,不到最后一刻就不创建实例。
public class MySingleton {
private static MySingleton instance = null;
private MySingleton(){} //私有化构造器
//public static方法提供获取途径。 直到调用的那一刻才创建实例
public static synchronized MySingleton getInstance(){
if (null == instance) {
instance = new MySingleton();
}
return instance;
}
}
懒汉式和饿汉式各有优劣(饿汉式天然同步,懒汉式延迟加载),我们寻找其他的途径就对它们两个的优劣进行取长补短。
3.双重检测锁式
因为编译器和CPU的优化,会导致指令的重排序。因此有可能出现先给instance分配内存,而new instance还没有产生实例的情况,此时instance==null不成立, 但是如果return instance而由外面对其操作,则会产生错误。
为了防止重排序,对instance 加上volatile,还使用了双重检测机制。
public Class Singleton{
private static volatile Singleton instance; //用volatile防止重排序
public static Singleton getInstance(){
if(null == instance){
synchronized (Singleton.class){ //在这里加锁是为了尽量减小加锁区域
//双重检测,为了防止JVM指令重排序导致先new而使得instance==null不成立
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
4.静态内部类式
要点:
(1)外部类没有static对象,不会像饿汉式一样立即加载对象;
(2)只有当调用getInstance时才会加载静态内部类。加载类时线程是安全的。并且instance是static final类型,保证了内存中只有一个对象实例存在且只能被赋值一次,保证了线程的安全性。
静态内部类式单例兼备了并发高效性和延迟加载的优点。
public class MySingleton{
private static class InnerClass{
private static final MySingleton instance = new MySingleton();
}
private MySingleton(){}
public static MySingleton getInstance(){
return InnerClass.instance;
}
}
5.枚举式
枚举本身就是单例模式,由JVM从根本上提供保障,避免了通过反射和反序列化调用和私有构造器的漏洞。 但不能延迟加载。
public enum MySingleton{
//定义枚举的一个元素,就是一个单例
INSTANCE;
//添加其他的操作
public void func(){
}
}