Java单例设计模式(Singleton)

面向对象的程序设计中,有时候,我们需要一些对象只有一个,比如线程池(threadpool),缓存(cache),对话框,等等。这些示例也只能有一个,如果有很多个,就会造成数据不统一,资源使用过量的结果。

在Java中,使用“单例设计模式”最简单的方法是:在class中实例化一个对象;将构造函数私有化(没有公开的构造函数,用户就不能再创建另一个对象了);提供通过class获得对象的方法(static)。

另外,如果这个对象比较耗资源,我们在程序中又不一定会用到它的话,那就太浪费了。所以一般我们会在程序用到单例的时候才第一次实例化这个对象。

上面这种实现的方法可能存在的问题是:如果多线程访问,那么对象还没有被实例化之前,可能会有两个线程同时进入getInstance()方法,一下子出来两个对象。

只要对这个方法加上synchronized关键字,就可以轻易地解决这个问题了。synchronized关键字可以保证,每个线程在进入这个方法之前,都必须等其他进程退出。也就是说,不可能同时有两个线程进入这个方法。

这样的确可以解决问题,但是又一个问题来了:性能降低。事实上,我们只需要第一次执行这个方法的时候,才需要同步。设置好单例对象,就不需要同步了,之后每次同步都是一种累赘。这个问题,有以下3个解决方法供参考:

1.如果getInstance()对程序的性能不是很重要,那么……就这样吧,这个方法简单又有效。

2.如果创建示例的代价不是很大,那么可以一开始就创建好示例。这样就可以解决多线程的问题(如此看来,并不是“用到时再创建”就一定好,也要看具体情况,实例化对象代价不大并且需要反复使用,那么就立即创建;如果对象消耗资源大且用到次数不多,再考虑惰性创建)。

3. 用双重检查加锁,先检查实例是否创建,如果未创建,才进入同步代码块创建实例。这样的话,只需要进入同步一次。

关于volatile关键字:volatile修饰的变量,jvm虚拟机保证从主内存加载到线程工作内存的值是最新的

在上面的代码中,用到单例的时候,多个线程可以同时检查是否存在对象,如果存在,以后都不用再进入同步代码了。只有第一次实例化的时候,才需要进入同步。即提高了性能,又节省了空间(惰性创建)。

参考资料:《Head First设计模式》



Java单例设计模式(Singleton)”已经有5条评论

    • 查了一下case关键字(http://www.imooc.com/article/5867) ,有个问题,case能保证不能第二次使用它创造伴生对象吗?别的地方还能使用类获得这个对象吗?

Leave a comment

您的电子邮箱地址不会被公开。 必填项已用 * 标注