为什么使用单例模式?
- 某些类创建比较繁琐耗时,频繁创建增加系统开销
- 省去了new操作符,降低了系统内存的使用频率,减轻GC压力
- 某些情况下要求该类有且仅有一个实例对象在当前JVM中 ,如资源管理相关 线程池 日志对象
特点
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
两种单例实现
懒汉式
懒汉式,只有当调用getInstance的时候,才会去初始化这个单例
|
|
此实现存在线程安全问题,有以下几种解决方案
在getInstance()方法上加synchronized
123456public static synchronized Singleton getInstance() {if (single == null) {single = new Singleton();}return single;}此种方式把锁加在方法上,这样每次调用此方法都会锁定整个对象,效率会比较低,实际上只有当单例不存在需要new的时候才需要上锁
双重检查锁定
12345678910public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗
静态内部类
1234567891011121314151617181920212223242526272829303132333435363738public class Singleton {/* 私有构造方法,防止被实例化 */private Singleton() {}/* 此处使用一个内部类来维护单例 */private static class SingletonFactory {private static Singleton instance = new Singleton();}/* 获取实例 */public static Singleton getInstance() {return SingletonFactory.instance;}}```> 此种方式 不需要同步来解决线程问题,而是利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,又避免了同步带来的性能影响。## 饿汉式> 饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的,而且天生是线程安全的```java//饿汉式单例类.在类初始化时,已经自行实例化public class Singleton1 {private Singleton1() {}private static final Singleton1 single = new Singleton1();//静态工厂方法public static Singleton1 getInstance() {return single;}}
区别
- 线程安全:
饿汉式天生就是线程安全的,可以直接用多线程环境而不出问题
懒汉式本身是非线程安全的,可以通过上述1、2、3方法解决 - 性能
饿汉式在类创建的同时就实例化出一个静态对象出来,不管以后是否使用都占用着内存,但是在其第一次调用速度也会非常快
懒汉式会延迟加载,只有在第一次使用的时候才会进行初始化,如果需要做的工作比较多,性能上会有影响