什么是单例
一个类在整个系统中只有一个实例,而且这个实例是在类的内部通过一个private的构造方法构造的,外部不能调用其构造方法,只能获取它的实例。
为什么使用单例而不是静态方法
静态方法是基于对象,单例是面向对象的,面相对象的代码提供一个更好的编程思想。
如果一个方法和他所在类的实例对象无关,那么它就应该是静态的,反之他就应该是非静态的。如果我们确实应该使用非静态的方法,但是在创建类时又确实只需要维护一份实例时,就需要用单例模式了。
单例的特点
1.单例类只能有一个实例。
2.单例类必须自己创建自己的唯一实例。
3.单例类必须给所有其他对象提供这一实例。
单例的饿汉式
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,它是线程安全的。
单例的懒汉式
懒汉式单例只有在使用时才会被实例化
但是上述懒汉式单例是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造并保证了懒汉式单例的线程安全。
在getInstance方法上加同步
如上,如果是在getInstance方法上加了一个synchronized,那么我每次去执行getInstace方法的时候都会受到同步锁的影响,这样运行的效率会降低,其实只需要在第一次创建Singleton实例的时候加上同步锁就可以了,那么便有如下的修改
这样效果是和直接在方法上加synchronized完全一致的。所以要在synchronized的外面再加一层判断
只有在single还没被初始化的时候才会进入到第3行,然后加上同步锁。等single一但初始化完成了,就再也走不到第3行了,这样执行getInstance方法也不会再受到同步锁的影响,效率上会有一定的提升。这种方法叫做双重锁定(Double-Check Locking)。
Double Check Lock 实现单例,优点是既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用 getInstance() 不进行同步锁;缺点是第一次加载反应稍慢。
静态内部类式
第一次加载不会初始化 Singleton,只有在第一次调用 Singleton 的 getInstance() 才会初始化,第一次调用会导致虚拟机加载 SingletonHolder 类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。
单例模式优缺点
优点:在内存中只有一个实例,减少了内存开支和性能开销;可以避免对同一资源文件的同时写操作;可以在系统设置全局的访问点,优化和共享资源访问。
缺点:一般没有接口,扩展困难;单例模式如果持有 Context,很容易引发内存泄漏,此时需要注意传递给单例对象的 Context 最好是 Application Context。