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

前言

此篇文章只是本人学习 单列模式(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))