1. 聚能聊>
  2. 话题详情

聊聊设计模式,我是皇帝我独苗

前面我们聊了:
什么是设计模式?你知道多少?
你必须知道的六大设计原则
今天我们来聊聊第一个设计模式,单例模式,也叫单件模式。
image

image
单件模式的类图可以说是所有模式的类图中最简单的,事实上,它的类图上只有一个类!但是,可不要兴奋过头,尽管从类设计的视角来说它很简单,但是实现上还是会遇到相当多的波折的。
image

优点
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。

一般Singleton模式通常有三种形式:
第一种形式:懒汉式,也是常用的形式。

public class SingletonClass{
    private static SingletonClass instance=null;
    public static synchronized SingletonClass getInstance(){
        if(instance==null){
               instance=new SingletonClass();
        }
        return instance;
    }
    private SingletonClass(){
    }
}

第二种形式:饿汉式

//对第一行static的一些解释
// java允许我们在一个类里面定义静态类。比如内部类(nested class)。
//把nested class封闭起来的类叫外部类。
//在java中,我们不能用static修饰顶级类(top level class)。
//只有内部类可以为static。
public class Singleton{
    //在自己内部定义自己的一个实例,只供内部调用
    private static final Singleton instance = new Singleton();
    private Singleton(){
        //do something
    }
    //这里提供了一个供外部访问本class的静态方法,可以直接访问
    public static Singleton getInstance(){
        return instance;
    }
}

第三种形式: 双重锁的形式。

public class Singleton{
    private static volatile Singleton instance=null;
    private Singleton(){
        //do something
    }
    public static  Singleton getInstance(){
        if(instance==null){
            synchronized(Singleton.class){
                if(instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
     }
}
//这个模式将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了。
//这种模式中双重判断加同步的方式,比第一个例子中的效率大大提升,因为如果单层if判断,在服务器允许的情况下,
//假设有一百个线程,耗费的时间为100*(同步判断时间+if判断时间),而如果双重if判断,100的线程可以同时if判断,理论消耗的时间只有一个if判断的时间。
//所以如果面对高并发的情况,而且采用的是懒汉模式,最好的选择就是双重判断加同步的方式。

那么:
1、它主要应用于哪些应用场景?

2、你做过的项目中采用单例模式了吗?你认为哪种实现形式最好?

3、你在单例模式的实现上遇到过什么问题吗?

4、你知道单例模式的注意事项吗?

参与话题

奖品区域 活动规则 已 结束

  • 奖品一

    阿里云代金券 x 3

  • 奖品二

    手机话费 x 2

  • 奖品三

    多功能工具箱 x 1

33个回答

3

北方的郎 已获得多功能工具箱 复制链接去分享

哈哈,工具箱不错啊。

1、它主要应用于哪些应用场景?
主要是各种资源管理器,以及一些公用的信息,比如:打印管理,日志管理,配置,参数,驱动,等等。

2、你做过的项目中采用单例模式了吗?你认为哪种实现形式最好?
用过,主要是用来管日志管理,配置,参数等。因为我做的系统对并发都有一定要求,所以我用的都是饿汉模式。

不过不能说饿汉式就一定比懒汉式的好。首先,懒汉式是典型的以时间换取空间的例子,就是每次获取实例时都要进行判断,看是否要创建实例,浪费判断时间。当然如果一直没有人用的话,就不会创建实例,则是节约空间。而饿汉式是典型的以空间换取时间,就是说当类装载的时候,就创建出一个实例,不管你用不用它,然后每次调用时就不用判断了,节省了运行时间。这里说某种方式一定比另一种方式好,它们两者各有各的优势。关键取决于你在时间和空间上效率的取舍。

3、你在单例模式的实现上遇到过什么问题吗?
因为用的比较注意,倒也没碰到什么问题。

4、你知道单例模式的注意事项吗?

因为GetInstance这个方法要用到该实例指针,且GetInstance这个方法是static的,所以这个指针必须是static的,否则GetInstance无法访问该实例指针。以此同时保证了向其他对象提供唯一的同一个内存区的实例指针。

单例模式的意图不只是为了节省资源,如果仅仅为了节省资源就使用单例模式的话可能造成单例模式的滥用。单例模式是为了确保在整个应用期间只有一个实例,以达到用户的特定的使用目的。比如windows操作系统里,有多个线程要同时进行文件创建、打开、修改一个文件的操作时,就用到单例模式设计文件管理器。所有的文件操作都必须同个这个唯一的实例来进行文件操作,避免的混乱的情况。

单例模式的坏处:
扩展困难,由于GetInstance静态函数没有办法生成子类的实例。如果要拓展,只有重写那个类。
隐式使用引起类结构不清晰。比如有时候,你并不知道某个类A是单例类,当你读类B的时候,你可能先看它头文件,或者类视图里的内容,从这里你无法知道A和B 关系,因为B类在实现的时候才使用A类的那个所谓的GetInstance函数,读不到这行,你就会知道B类对A类的依赖关系。
导致程序内存泄露的问题。很多人只是调用了GetInstance生成唯一的实例,却永远new被封装在GetInstance里忘了去释放内存。

什么情况下不能用单例模式:
单例模式简单易用,但是也是所有设计模式中最容易滥用的模式。当你的类想得到很好的扩展时,不能使用单例模式。
也许你的程序一开始并非一定要确保只有一个实例,如果你仅仅是为了节省资源而用的话,这个时候要慎用,因为随着时间的推延也许你的程序还需要扩展。

花兰 回复

大神这一段话没看懂能给解释一下嘛?
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中,只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。

评论
0

花兰 已获得手机话费 复制链接去分享

谁能帮我解答这个问题:
单例设计模式的双重校验锁方式 volatile有俩个作用
1.保证修饰变量的可见性
2.禁止指令重排序(有序性)
而synchronized作用也可以保证
1.保证变量的可见性
2.禁止指令重排序 (有序性)
3.原子性
那为什么双重校验锁还要加volatile
谁能给我解释下,谢谢了。

我对synchronize的理解可能有偏差,以上是从深入理解JAVA虚拟机看到的。

微wx笑 回复

个人觉得 volatile 的使用是更好降低锁竞争

评论
1

cjsoldier 已获得阿里云代金券 复制链接去分享

1、它主要应用于哪些应用场景?

只需要一个类的实例的时候,或者说只应该有一个类的实例的时候。
我的理解就是全局共享一个实例。为了保证这个实例是唯一的,所以用单例模式。

2、你做过的项目中采用单例模式了吗?你认为哪种实现形式最好?

用了,我认为在聊主提到的3种实现方式,饿汉式最好。因为它最简单而且不容易出问题。只是不容易出问题而已。
除此之外单例模式在Java里的最佳实践是用枚举来实现。简单高效,可谓鱼和熊掌兼得。

3、你在单例模式的实现上遇到过什么问题吗?
我没遇到过,不过我关注过单例,因为网上有很多文章,单例看起来简单,其实是有很多坑的,多到让人怀疑人生。
我都直接用最佳实践了。

4、你知道单例模式的注意事项吗?
设计模式是跟语言无关的。但是使用设计模式的必然涉及到语言,这里只说在咱们Java中需要注意的地方:

①别把构造函数暴露出来。就是说构造函数一定要私有化。这点最重要。

②防止反射。使用反射和setAccessible(true)方法,仍然可以创建其他新的实例。使用反射的时候要注意看看这个类是不是单例的哟。

③使用静态工厂实现的饿汉式,在类被加载的时候就会实例化一次。有2个地方需要注意:
1)可能会有一点点浪费,因为有可能永远不会使用这个实例。
2)如果类被多次加载的话也会造成多次实例化,这个问题可以使用静态内部类来解决。

④懒汉式在多线程环境下会出问题,这个地球猿都知道了,所以才有了双重锁机制

⑤双重锁机制在jdk5之前会用潜在问题(java内存模型有关)。jdk5可以加一个volatile解决。jdk5之后不需要加volatile了。不过还是有一个问题:
在反序列化的时候会得到多个对象。所以需要在单例类中加一个readResolve方法,这样反序列化的时候得到的还是以前的那个对象。

花兰 回复

你说jdk1.5后双重校验锁方式 不用volatile关键字了,确定吗?你是怎么理解的能讲讲吗

cjsoldier 回复
回复@花兰:

网上看到的,不对的话请指教。

评论
1

沙漠的热情 已获得阿里云代金券 复制链接去分享

1、它主要应用于哪些应用场景?
当一个类的实例可以有且只有一个的时候就需要用到了。为什么只需要有一个呢?我认为使用单例模式的时机是当实例存在多个会引起程序逻辑错误的时候。比如类似有序的号码生成器这样的东西,怎么可以允许一个应用上存在多个呢?

2、你做过的项目中采用单例模式了吗?你认为哪种实现形式最好?
事物都是两面,不存在所谓的最好吧,还是活学活用的好!

3、你在单例模式的实现上遇到过什么问题吗?
没有用过啊

4、你知道单例模式的注意事项吗?
从具体实现角度来说,就是以下三点:
一是单例模式的类只提供私有的构造函数;
二是类定义中含有一个该类的静态私有对象;
三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。

1

浮生递归 已获得阿里云代金券 复制链接去分享

1、它主要应用于哪些应用场景?
网站的计数器、应用程序的日志应用、Web应用的配置对象的读取、数据库连接池的设计、多线程的线程池的设计

2、你做过的项目中采用单例模式了吗?你认为哪种实现形式最好?
单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉。
单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式。

3、你在单例模式的实现上遇到过什么问题吗?
写一个单例,多线程并发的时候怎么处理。

4、你知道单例模式的注意事项吗
如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

0

小不点02 已获得手机话费 复制链接去分享

1、它主要应用于哪些应用场景?
一个类只能有一个实例,一个程序只能运行一个。

2、你做过的项目中采用单例模式了吗?你认为哪种实现形式最好?
采用了,当这个类在程序中一定要用到的时候,饿汉式更好吧。

3、你在单例模式的实现上遇到过什么问题吗?
没有诶

4、你知道单例模式的注意事项吗?
建议使用饿汉式单例,防止发生线程不安全的情况,在高并发的情况下可能会发生不安全的情况。假如饿汉里面没有final,由于类的初始化需要一个过程,可能会创建多个类。

0

1762421291856958 复制链接去分享

单件模式是什么呢?

微wx笑 回复

单例模式,也叫单件模式

评论
0

1345414731456661 复制链接去分享

新人来了

微wx笑 回复

欢迎

评论
0

1001821107250717 复制链接去分享

一个虚拟主机可以挂几个域名

微wx笑 回复

要看备案的时候允许绑定多少个

评论
0

fidie-222 复制链接去分享

新人报道

微wx笑 回复

欢迎

评论
0

1809721220988242 复制链接去分享

我来了,小白一个,比白纸还白,一脸蒙蔽

微wx笑 回复

谁都小白过

评论
0

花兰 复制链接去分享

谁能帮我解答下
线程一进去synchronized代码块里然后修改了变量a的值后,退出synchronized代码块后一定会a同不会主内存,这点是肯定的。但会不会导线程二工作内存中a的无效?无效就从主内存获取最新的a,否则就用过期的a

0

德金 复制链接去分享

视频播放器,保证全局只有一个视频在播放

0

1331517681513347 复制链接去分享

你好

0

1751519925152201 复制链接去分享

初学,原来一直习惯的是懒汉模式啊,感谢!

0

1883421392159549 复制链接去分享

主要看你自己的思维方式在于你个人

0

1690321386557273 复制链接去分享

是说的什么

0

1903417723337938 复制链接去分享

0

1791521033842879 复制链接去分享

这个需要服务器吗?

0

1939621274477689 复制链接去分享

……

2