单例模式比较常见,常见于很多场景,比如:Spring容器中默认管理的bean是单例, 数据库的连接池一般也是设计成单例(数据库连接是一种资源),还有windows回收站等等。
优点:
由于只生成一个实例,所以可以减少系统开销。当某个对象的产生需要比较多的资源时,就可以通过在应用启动产生一个单例对象常驻内存,减少对象创建带来的开销。
单例模式的特点:
1 某个类只能有一个实例
2 自行创建这个实例
3 对外提供这个实例的方法
常见种类:
饿汉式(线程安全, 可能存在不需要使用该类造成资源浪费):
/** * 饿汉式单例 * 1. 类被加载时,instance就会被实例化,私有的构造器调用。所以可能存在该类不会被使用但也创建了该对象。 */ public class SingletonDemo01 { private static final SingletonDemo01 instance = new SingletonDemo01(); //私有化构造器 private SingletonDemo01(){ } //静态工厂方法 public static SingletonDemo01 getInstance(){ return instance; } }
懒汉式(非线程安全,延迟加载资源合理利用):
/** * 懒汉式模式 * 1 和饿汉式相比,类构造器都是私有。 * 2 延迟加载,使用时在实例化 */ public class SingletonDemo02 { //默认不初始化 private static SingletonDemo02 instance; private SingletonDemo02() { } //第一次使用的时候初始化,但调用时需要保证同步,调用效率不高 public static synchronized SingletonDemo02 getInstance() { if (instance == null) { instance = new SingletonDemo02(); } return instance; } }
由上面懒汉式可能在其他地方见过一种双重检测锁机制优化懒汉式模式,其实是不正确的
/** *双重检测锁机制,看似可行但由于JIT编译的时候会有优化(不能移出synchronized中的,但可以移入。。。),成getInstance2() * 将会在执行时可能遇到由于指令重排序产生问题 */ public class SingletonDemo03 { //默认不初始化 private static SingletonDemo03 instance; private SingletonDemo03() { } public static SingletonDemo03 getInstance() { if (instance == null) { synchronized (SingletonDemo03.class){ SingletonDemo03 inst = instance; if(inst == null){ synchronized (SingletonDemo03.class){ inst = new SingletonDemo03(); } } instance = inst; } } return instance; } public static SingletonDemo03 getInstance2() { if (instance == null) { synchronized (SingletonDemo03.class){ SingletonDemo03 inst = instance; if(inst == null){ synchronized (SingletonDemo03.class){ instance = new SingletonDemo03(); } } // instance = inst; } } return instance; } }
静态内部类模式(线程安全, 延迟加载):
/** * 静态内部类模式 * * 1. 外部类没有static属性(不会立即加载) * * 2. 调用getInstance才会加载 * * 3. 利用静态类加载线程安全特性保证线程安全 */ public class SingletonDemo04 { private static class SingletonInstance { private static SingletonDemo04 instance = new SingletonDemo04(); } private static SingletonDemo04 getInstance(){ return SingletonInstance.instance; } private SingletonDemo04() { } }
这种方式比较推荐
枚举模式(线程安全, 无延迟加载,防止反射和反序列化漏洞):
/** * 枚举类模式 * 1. 天然就是单例 * */ public enum SingletonDemo05 { //实例 INSTANCE; public void otherMethods(){ System.out.println("Something"); } }
public class Client { public static void main(String[] args) { SingletonDemo05 s1 = SingletonDemo05.INSTANCE; SingletonDemo05 s2 = SingletonDemo05.INSTANCE; System.out.println(s2 == s2); } }
枚举类优势在于简单。
单例的枚举实现在《Effective Java》中有提到,因为其功能完整、使用简洁、无偿地提供了序列化机制、在面对复杂的序列化或者反射攻击时仍然可以绝对防止多次实例化等优点,被作者认为是实现Singleton的最佳方法。