单例模式

来源:互联网 发布:淘宝怎么修改店铺类目 编辑:程序博客网 时间:2024/06/05 20:34

单例模式

主要对单例模式进行介绍,还有Java常用的单例模式实现方式:

  • 单例模式特点
  • 懒汉式
  • 线程安全的懒汉式
  • 饿汉式
  • 通过枚举实现单例
  • 懒汉式、饿汉式对比

单例模式特点

  • 单例类在一个系统中只能有一个实例
  • 系统中,其他对象使用的都是同一个实例
  • 单例类必须自己创建自己的实例

懒汉式单例

懒汉式单例,因为懒汉,所以在第一次使用到实例的时候才进行实例化

/** * ProjectName:DesignPattern * PackageName:com.qzj.Singleton * Date:2017/6/7 9:39 * 懒汉式单例模式,在第一次调用的时候实例化单例对象 * * 此方法没有考虑线程安全问题,并发情况下可能会产生多个instance, * 故此,存在三种改进方法:同步锁,双检查锁定,静态内部类 */public class LazyModeSingleton {    private LazyModeSingleton() {    }    private static LazyModeSingleton instance = null;    //普通懒汉式方法    public static LazyModeSingleton getInstance() {        if (instance == null) {            instance = new LazyModeSingleton();        }        return instance;    }}

普通的懒汉式单例存在一个问题,在高并发情况下,多个线程同时执行getInstance()方法获取实例,可能会产生多个instance,故此懒汉式单例需要进行线程安全的优化。

线程安全的懒汉式

1.同步锁

为解决懒汉式的线程安全问题,主要思路就是控制同一时刻只能有一个线程来进行实例的创建,所以加锁实现同步方法成为了一种办法。

/** * ProjectName:DesignPattern * PackageName:com.qzj.Singleton * Date:2017/6/7 9:51 * <p> * 通过加上同步,处理懒汉模式的单例 */public class LazyModeSingletonSync {    private LazyModeSingletonSync() {    }    private static LazyModeSingletonSync instance = null;    //方法上加上同步    public synchronized static LazyModeSingletonSync getInstance() {        if (instance == null) {            instance = new LazyModeSingletonSync();        }        return instance;    }}

2.双检查锁

第一种方式可以解决线程同步问题,但是因为synchronized加在方法上,所以对性能影响很大,不推荐使用。双检查是先判断实例是否存在,不存在话在同步块里进行实例化,不需要进行方法的同步,这种方式保证synchronized只会在首次创建实例的时候被使用到。

/** * ProjectName:DesignPattern * PackageName:com.qzj.Singleton * Date:2017/6/7 9:56 * <p> * 双重检查锁定来优化懒汉式单例 * * 此种方式既可以实现线程实例安全的创建,又不会对性能造成大的影响 * 由于volatile关键词会屏蔽虚拟机一些代码优化,效率并不高,建议没有特别的需要不要使用 * 也就是说,虽然可以通过这种凡是实现线程安全的单例,并不建议大量采用 */public class LazyModeSingletonDoubleCheck {    private LazyModeSingletonDoubleCheck() {    }    //“双重检查加锁”机制的实现会使用关键字volatile,    // 它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。    private volatile static LazyModeSingletonDoubleCheck instance = null;    public static LazyModeSingletonDoubleCheck getInstance() {        //先检查实例是否存在,不存在才进入同步块        if (instance == null) {            //线程安全的创建实例            synchronized (LazyModeSingletonDoubleCheck.class) {                //再次检查实例是否存在                if (instance == null) {                    instance = new LazyModeSingletonDoubleCheck();                }            }        }        return instance;    }}

3.通过静态内部类实现

前两种方式虽然都可以解决懒汉式单例线程安全的问题,但是都对性能有一定的影响,但是通过静态内部类来实现的懒汉式就可以避免这部分性能损耗,静态内部类主要是借助虚拟机类加载保证实例的唯一和线程安全。

/** * ProjectName:DesignPattern * PackageName:com.qzj.Singleton * Date:2017/6/7 10:06 * <p> * 通过静态内部类的方式优化懒汉式单例模式 * * 此方法优于同步实现和双检查方式,即实现了线程安全,又避免了同步带来的性能问题, * * 当getInstance方法被第一次调用时,它第一次读取 SingletonHolder.instance,导致SingletonHolder类得到初始化; * 而这个类在装载并被初始化的时候,会初始化它的静 态域, * 从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。 * * 这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。 */public class LazyModeSingletonInnerClass {    private LazyModeSingletonInnerClass() {    }    /**     * 内部类,静态成员式的内部类     * 此内部类和外部类实例没有绑定关系,只有调用的时候才会初始化,从而实现了延迟加载     */    public static class SingletonHolder {        /**         * 静态初始化器,由JVM来保证线程安全         */        private static LazyModeSingletonInnerClass instance = new LazyModeSingletonInnerClass();    }    public static LazyModeSingletonInnerClass getInstace() {        return SingletonHolder.instance;    }}

饿汉式单例

饿汉式单例,因为是饿汉,所以在类初始化的时候就实例化好了单例实例,也正是因此饿汉式单例本生就是线程安全的。

/** * ProjectName:DesignPattern * PackageName:com.qzj.Singleton * Date:2017/6/7 10:25 * * 饿汉模式单例:在类初始化时,已经实例化 * * 饿汉模式在类创建的时候就创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的 */public class HungryModeSingleton {    private HungryModeSingleton() {    }    private static HungryModeSingleton instance = new HungryModeSingleton();    public static HungryModeSingleton getInstance() {        return instance;    }}

通过单元素枚举实现单例

为什么会通过枚举来实现单例?
参考:https://segmentfault.com/q/1010000003732558

/** * ProjectName:DesignPattern * PackageName:com.qzj.Singleton * Date:2017/6/7 15:51 * * 使用枚举来实现单例会更加简洁,而且无偿提供了序列化机制, * 并由JVM从根本上提供保障,防止多次实例化,是简洁、高效、安全的实现单例的方式 */public enum EnumModeSingleton {    INSTANCE;    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}class Test {    public static void main(String[] args) {        EnumModeSingleton instance = EnumModeSingleton.INSTANCE;        instance.setName("tom");        System.out.println("instance name is" + instance.getName());        EnumModeSingleton instance2 = EnumModeSingleton.INSTANCE;        System.out.println("instance2 name is"+instance2.getName());    }}

懒汉式、饿汉式对比

1.实例化时间:饿汉式类加载的时候就开始实例化,懒汉式首次调用的时候实例化

2.线程安全:饿汉式本身线程安全,懒汉式需要特殊处理下线程安全

3.性能和占用资源:饿汉式类加载就实例化,资源会持续占用,懒汉式需要的时候才实例化占用资源,
但是响应的需要使用实例时时候,饿汉式因为已经实例化好了会快速响应,而懒汉式若此首次调会影响性能。