单例模式

一 什么是单例模式

     单例模式是Java编程中最常用的设计模式之一,所谓单例就是在JVM(Java虚拟机)中时刻只有一个对象实例存在,那么它的好处也很明显,有如下几点:

  1. 某些类创建比较频繁,特别是大型的对象,如果使用单例模式,将会大大减少系统的开销;
  2. 减少New操作,降低系统内存的使用频率,减少GC压力;
  3. 避免系统混乱,一些类必须使用单例模式,例如控制交易流程的类等。

二 单例模式

1 饿汉式单例模式

public class Singleton {
    private static Singleton  singleton  = new Singleton();
    public static Singleton getInstance() {
        return singleton;
    }
    /**
     * 杜绝单例对象被反序列化时重新生成对象
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        return singleton;
    }

}

说明,在这种模式下,在Singleton类被加载时就对singleton对象进行实例化,不存在多线程同步的的问题,但在加载该类的过程会比较缓慢,也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到懒加载的效果。

2 懒汉式单例模式(线程不安全)

public class SingletonLazy {
    
    private static SingletonLazy singleton  = null ;

    /**
     * 构造方法私有化
     * */
    private  SingletonLazy(){

    }
    public static SingletonLazy getInstance() {
        if (null == singleton){
            singleton = new SingletonLazy();
        }
        return singleton;
    }
    /**
     * 杜绝单例对象被反序列化时重新生成对象
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        return singleton;
    }
}

说明,在这种模式下,在Singleton类被加载时没有对singleton进行实例化,调用getInstance方法,才对singleton进行实例化,但在处理多线程同步时,会出现问题,线程不安全。

3 方法名上加同步锁单例模式(线程安全)

public class SingletonLazySyn {

    private static SingletonLazySyn singleton  = null ;

    /**
     * 构造方法私有化
     * */
    private SingletonLazySyn(){

    }

    /**
     * 使用关键字synchronized 锁方法
     * @return
     */
    public static synchronized SingletonLazySyn getInstance() {
        if (null == singleton){
            singleton = new SingletonLazySyn();
        }
        return singleton;
    }
}

说明,在这种模式下,在一定程度上解决了多线程不安全的问题,但是在每次调用getInstance方法时,都会给方法上锁,而实际上只有第一次创建该类实例需要上锁,大部分是不需要的,这样一来就影响了应用的性能,不推荐使用这种方法。

4 双重检查单例模式

public class SingletonLazySyn2 {

    private static SingletonLazySyn2 singleton  = null ;
    /**
     * 构造方法私有化
     * */
    private SingletonLazySyn2(){

    }

    /**
     * 使用关键字synchronized 锁对象
     * @return
     */
    public static  SingletonLazySyn2 getInstance() {
        if (null == singleton){
            synchronized (SingletonLazySyn2.class){
                if (null == singleton){
                    singleton = new SingletonLazySyn2();
                }
            }
        }
        return singleton;
    }

    /**
     * 杜绝单例对象被反序列化时重新生成对象
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        return singleton;
    }
}

说明,在这种模式下,当singleton 为null 才创建该类实例,创建实例的时候会给该类加上同步锁,这样看似既解决了性能的问题、又解决了多线程不安全的问题,但是这也不是绝对的安全,由于在JVM中,Java指令对创建实例和赋值是分两步进行,其间JVM不能保证这两个操作执行的先后顺序,所以就可能会出现异常,例如两个线程threadA、threadB同时执行到if判断,然后会进行如下操作:

->如果threadA先到synchronized块,判断singleton为null,会执行singleton = new SingletonLazy2();

->然后threadB执行到synchronized块,但此时该类已经加上同步锁,该线程此时被阻塞不能往下执行;

->JVM将为新的对象分配内存,并赋值给singleton,注意此时如果JVM尚未初始化这个实例,线程threadA离开synchronized;

->此时threadB可以进入到synchronized块,判读判断singleton不为null,那么直接返回singleton(实际上singleton尚未初始化);

->threadB获取singleton后,拿来使用时,将会出现异常。

5 静态内部类单例模式

public class SingletonFac {
    /**
     * 构造方法私有化
     * */
    private  SingletonFac(){
    }

    public static SingletonFac getInstance() {
        return SingletonFactory.singleton;
    }

    public static  class  SingletonFactory {
        private static SingletonFac singleton  = new SingletonFac();
    }

    /**
     * 杜绝单例对象被反序列化时重新生成对象
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        return getInstance();
    }

}

说明,在这种模式下,可以解决上述问题,在加载SingletonFac时不会对singleton进行实例化,在加载SingletonFactory类(调用getInstance方法)才会对其进行实例化,而且能够保证自始自终JVM只有一个singleton实例,也不存在多线程不安全的问题,所以这个是使用最多的。

6 登记式单例模式

public class SingletonMap {
    /**设立静态变量,记录单例模式对象实例*/
    private static Map<String, Object> map = new HashMap<String, Object>();

    private SingletonMap  (){

    }

    public static Object getInstance(String name) {
        if (TextUtils.isEmpty(name)){
            name = SingletonMap.class.getName();
        }
        if (map.containsKey(name) && null != map.get(name)){
            return map.get(name);
        }else {
            try {
                map.put(name,Class.forName(name).newInstance());
            } catch (InstantiationException e) {
                Log.e(SingletonMap.class.getSimpleName() , e.getMessage());
            } catch (IllegalAccessException e) {
                Log.e(SingletonMap.class.getSimpleName() , e.getMessage());
            } catch (ClassNotFoundException e) {
                Log.e(SingletonMap.class.getSimpleName() , e.getMessage());
            }
            return map.get(name);
        }
    }

}

说明,这个类用来管理、记录单例模式对象,当需要某个单例模式对象时,首先在map中去查找,如果存在直接取出返回;否则就通过class.forName(name).newInstance()创建实例,这样能够保证JVM始终只有一个name对应类的实例。

public class SingletonMapChild {
    /**
     * 使用关键字synchronized 锁方法
     * @return
     */
    public static synchronized SingletonMapChild getInstance() {
        return (SingletonMapChild) SingletonMap.getInstance(SingletonMapChild.class.getName());
    }

    /**
     * 杜绝单例对象被反序列化时重新生成对象
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        return getInstance();
    }
}
说明,获取SingletonMapChild实例通过静态方法getInstance,其真正创建该类实例的过程是在SingleMap中的getInstance方法中。

参考:

https://blog.csdn.net/lanzhizhuxia/article/details/7922977

https://blog.csdn.net/itachi85/article/details/50510124


  





     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值