美文网首页
2--∞:单例模式(Singleton Pattern)

2--∞:单例模式(Singleton Pattern)

作者: Gakki的伍记 | 来源:发表于2017-03-01 15:41 被阅读14次

    前言

    此篇文章只是本人学习 单列模式(Singleton Pattern) 使用的笔记,如有雷同,纯属缘分!

    什么是单例?

    单例模式(Singleton Pattern):确保某一个类只有一个实例,并提供该实例的全局访问,其构造函数私有化。它是 Java 中最简单的设计模式(Design Pattern)。

    设计模式(Design Pattern):前人对特定问题经过无数次的经验总结之后 ,提出的能够解决它的优雅的方案。它不是一种技术与方法,而是一种思想!

    本质:

    控制实例数量

    三大要点:

    线程安全
    延迟加载
    序列化与反序列化安全

    单例的几种写法

    1.饿汉式
    public class Singleton {
        //上去就是干,直接实例化
        private static Singleton instances = new Singleton();
        //私有化构造函数,阻止实例化对象
        private Singleton() {}
        //直接返回已经实例化了的对象
        public static Singleton getInstances() {
            return instances;
        }
        public void doSomething() {
            //doSomething....
        }
    }
    

    饿汉式 这种实现单例方法是最简单粗暴的,它在类开始加载时就初始化了(但浪费内存),而且在多线程中是安全的,是典型的空间换时间。如果单例对象初始化非常快,而且占用内存非常小的时候,用这种方式是比较合适的,可以直接在应用启动时加载并初始化。

    2.懒汉式,线程不安全
    public class Singleton {
        private static Singleton instances = null;
        private Singleton() {}
        //如果发现没有实例对象,就构造一个;如果有实例对象,直接返回
        public static Singleton getInstances() {
            if (instances == null) {
                instances = new Singleton();
            }
            return instances;
        }
        public void doSomething() {
            //doSomething
        }
    }
    

    懒汉式 就是将单例的初始化操作,延迟加载 到需要的时候才进行,这样做在某些场合中有很大用处。比如某个单例用的次数不是很多,但是这个单例提供的功能又非常复杂,而且加载和初始化要消耗大量的资源,这个时候使用懒汉式就是非常不错的选择。但它在 多线程中不安全(比如,有两个线程,一个是线程 A,一个是线程 B,它们同时调用 getInstances 方法,就可能导致并发问题),是典型的时间换空间

    3.懒汉式,线程安全
    public class Singleton {
        private static Singleton instances = null;
        private Singleton() {}
        public static Singleton getInstances() {
            //加入同步锁 synchronized
            synchronized (Singleton.class) {
                if (instances == null) {
                    instances = new Singleton();
                }
                return instances;
            }
        }
        public void doSomething() {
            //doSomething
        }
    }
    

    这种是最常见的解决同步问题的一种方式,使用同步锁 **synchronized (Singleton.class) **防止多线程同时进入造成 getInstances 被多次实例化。

    4.懒汉式,双重校验锁
    public class Singleton {
        // 对保存实例的变量添加 volitile 的修饰
        private volatile static Singleton instances = null;
        private Singleton(){}
        public static Singleton getInstances(){
            //先检查实例是否存在,如果不存在才进入下面的同步块
            if(instances == null){
                //同步块,线程安全的创建实例
                synchronized (Singleton.class) {
                    //再次检查实例是否存在,如果不存在才真正的创建实例
                    if (instances == null){
                        instances = new Singleton();
                    }
                }
            }
            return instances;
        }
        public void doSomething() {
            //doSomething
        }
    }
    

    双重校验锁 指的是:并不是每次进入 getInstances 方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例。这是第二重检查。

    双重加锁机制的实现会使用一个关键字 volatile ,它的意思是:被 volatile 修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

    5.静态内部类
    public class Singleton {
        //类级的内部类,也就是静态类的成员式内部类,该内部类的实例与外部类的实例
        //没有绑定关系,而且只有被调用时才会装载,从而实现了延迟加载
        private static class SingletonHolder {
            //静态初始化器,由JVM来保证线程安全
            private static Singleton instances = new Singleton();
        }
        private Singleton() {}
        public static Singleton getInstances() {
            return SingletonHolder.instances;
        }
        public void doSomething() {
            //doSomething
        }
    }
    

    这样实现出来的单例类就是线程安全的,而且使用起来非常简洁。

    6.枚举类型单例模式
    public enum Singleton{
        //定义一个枚举的元素,它就是Singleton的一个实例
        instance;
        public void doSomething(){
            // do something ...
        }    
    }
    

    这种方法是根据 Effective Java 书中的说法,默认枚举实例的创建是 线程安全 的.(创建枚举类的单例在 JVM 层面也是能保证线程安全的), 所以不需要担心线程安全的问题,所以理论上枚举类来实现单例模式是最简单的方式。

    总结

    以上就是 单例模式 的几种写法。分别是饿汉懒汉同步锁双重校验锁静态内部类枚举。我们可以根据不同的场景选择最喜欢的一种单例模式吧!

    如何使用

    当需要控制一个类的实例只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式,这些功能恰好是单例模式要解决的问题

    番外阅读

    单例模式的一些注意点

    参考

    ANDROID设计模式之单例模式
    设计模式干货系列:(四)单例模式【学习难度:★☆☆☆☆,使用频率:★★★★☆】

                                            -- 默默的划水ing--

    相关文章

      网友评论

          本文标题:2--∞:单例模式(Singleton Pattern)

          本文链接:https://www.haomeiwen.com/subject/qruupttx.html