单例没你想的那么简单

单例没你想的那么简单

单例是最简单的几种设计模式之一,但它远没有你想的那么简单!

==============

应用

简单的提一下,哪里需要使用单例:线程池、缓存、网络等等需要保持全局作用域且唯一的对象。

但是这里需要提一下,什么不适合申明为单例:View。View是比较重的对象,占内存较普通对象更多,而View更可能引用其他资源,而导致其他资源也无法被释放。

懒汉:

private static NetWork instance;

public static NetWokr getInstance({
    if(instance == null){
        instance = new NetWork();
    }
    return instance;
)

饿汉

private static NetWork instance = new NetWork();

public static NetWokr getInstance(){
    return instance;
)

单例简单,单例的两种申明方式大家也都知道。但知道如何使用这两种申明方式的可就不多了。

最普通的一种解释是:

  • 如果这个对象并不着急使用,或者这个对象占据资源较多的话,就使用懒汉来延迟加载。

  • 如果这个资源创建时间很长,就提前创建。

其实这个解释并不能让我很满意,哪些对象占据资源特别多?创建时间特别长的对象又是什么?真正遇到这样的场景其实并不多,所以大部分时候我们还是看心情来……

懒汉与饿汉的区别

懒汉可以保证我们的单例在需要的时候才被初始化。这是一个优点,也是优于单纯的静态变量的一个点。

但懒汉在多线程环境下,有可能会出现问题。

在未加锁的情况下,两个线程同时 getInstance(),然后就创建新对象。这就导致了系统中有两个单例存在,这是很危险的一件事,容易导致非常奇怪而且找不到问题的Bug。

解决这个问题有两个简单办法:

  • 在getInstance方法加synchronized 修饰。但这也可能因为多线程的同步问题拖垮性能。

    public static synchronized NetWokr getInstance({
        if(instance == null){
            instance = new NetWork();
        }
        return instance;
    )
    
  • 使用饿汉方式。但也有问题,也许初始化了的单例,一直都没有被用到。

利用双重检查加锁机制实现高效的多线程单例
private volatile static NetWork instance;

public static NetWokr getInstance({
    //检查是否未创建,如果未创建才加锁
    if(instance == null){
        //只有第一次未创建才会进入到这里
        synchronized(NetWork.class)
        {
            //进入后再检查一次,如果为Null才创建实例
            if(instance == null){
                instance = new NetWork();
        }
        }

    }
    return instance;
)

这样写是不是有种眼前一亮的感觉?

单例与静态类的区别

单例和静态类的区别一直都是大多数人纠结的问题,因为乍一看,他们就是一样的嘛。

下面总结一下单例与静态类的区别:

  • 静态类在最开始就初始化了,单例可以延迟加载
  • 全局变量并不能保证只有一个实例

    在多个类加载器同时存在的情况下,就有可能导致多个单例存在的奇怪现象。

  • 单例依然是可以继承类实现接口,依然可以实现面向对象特性。而静态类就不可以了。

那这么说,静态类岂不完全没有市场了?

不是的,就拿工具类来说,静态类就比单例合适。

当所有的方法都被申明为静态方法以后,我们用这个类是不需要创建对象的消耗内存的。

而要使用单例,反而还要多开销一个内存。

单例的缺点

上面都在说单例的优点了,最后就提一下单例的缺点吧。为什么只是提一下呢?因为单例真的比较优秀,不太好挑刺啊。

缺点1: 因为是静态的,所以会一直常驻在内存中,如果这个单例的使用频率很低,就是一种浪费了。自己掌握其申明周期是一个不错的选择。

缺点2: 申明了两个单例以上,这个Bug是致命的,因为你很难会想到问题在这上面,就类似如香蕉脱了衣服把自己滑到这种事情。

缺点3: java的GC和引用机制,非常用以导致OOM,单例的生命周期是整个应用的生命周期,所以单例所引用的对象也将会是整个应用的生命周期。如果使用不当,就将导致内存溢出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值