【设计优化】- 正确使用单例模式

来源:互联网 发布:4 2网络协议转换器 编辑:程序博客网 时间:2024/05/29 13:48

单例模式是设计模式中使用最为普遍的模式之一。它是一种对象创建模式,用于产生一个对象的具体事例,它可以确保系统中一个类只产生一个实例。这种行为能带来两大好处:

(1) 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔开销。

(2)由于 new 操作的次数减少,因而对内存的使用频率也会降低,减轻了GC压力,缩短了GC时间。

一个简单的单例模式实例如下:

public class Singleton {private Singleton() {System.out.println("Singleton is create");}private static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}}

有两点需要注意的地方,首先是需要有一个私有的构造函数,然后要有一个静态的instance变量和getInstance方法。

注意:这里的getInstance方法不用进行同步,因为虚拟机加载类机制保证了只会产生一个Singleton,所有的线程读这个Singleton依然是线程安全的。

这种方法实现简单,而且十分可靠,但是也有缺点,就是实例不是延迟加载的。假如单例创建过程很慢的话,那么虚拟机在加载单例类的时候就会初始化单例对象。比如该单例类扮演着创建字符串的角色,而当系统只使用单例创建字符串而不用它的单例对象时,该单例对象也会被创建,在一定程度上会浪费系统资源。

为了实现延迟创建单例对象,并且只有在使用的时候才创建,我们可以写出下面一种:

public class LazySingleton {private LazySingleton() {System.out.println("Singleton is created");}private static volatile LazySingleton lazySingleton;public static LazySingleton getInstance() {if (lazySingleton == null) {synchronized (lazySingleton) {if (lazySingleton == null) {return lazySingleton = new LazySingleton();}}}return lazySingleton;}

注意:这里的同步是必要的,且必须将lazySingleton声明为volatile类型,不然可能会发生生成多个单例对象或者是读取到还未执行<init>初始化,状态错误的单利对象。

这种加了同步的方式虽然解决了延迟加载的功能,但是却引入了同步关键字。因此在多线程的环境中,它的时间消耗远远大于第一种方式。经过测试,启动10个线程,每个线程进行1000次getInstance操作,可以得到下面的性能数据:

第一种方式:


课件工话费时间0.098636s。

第二种方式:


共花费0.443623s。

性能差距5倍,还是挺大的。

为此我们可以使用内部静态类来优化性能问题。

public class StaticSingleton {private StaticSingleton() {System.out.println("Singleton is created");}private static class SingletonHolder {private static StaticSingleton instance = new StaticSingleton();}public static StaticSingleton getInstance() {return SingletonHolder.instance;}}

只有在首次调用getInstance() 方法时,SingletonHolder类才会加载,从而初始化instance对象。同时,由于实例的创建是在类加载时完成的,天生对线程友好,getInstance() 方法也不需要使用同步关键字。

通常情况下,上述实现方式已经能保证系统中只存在唯一的实例了。但是,仍然有例外的情况导致系统生成多个实例,比如,在代码中,通过反射机制,强行调用单例类的私有构造函数,生成多个实例。使用反序列化的方式生成新对象。


0 0
原创粉丝点击