单例模式

当使用Singleton.createString()执行任务时,程序输出:

Singleton is create
createString in Singleton
可以看到,虽然此时并没有使用单例类,但它还是被创建出来,这也许是开发人员所不愿意见到的。为了解决这个问题,并以此提高系统在相关函数调用时的反应速度,就需要引入延迟加载机制。

public class LazySingleton {
private LazySingleton(){
System.out.println("LazySingleton is create");
//创建单例的过程可能会比较慢
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance() {
if (instance==null)
instance=new LazySingleton();
return instance;
}
}
首先,对于静态成员变量instance初始值赋予null,确保系统启动时没有额外的负载;其次,在getInstance()工厂方法中,判断当前单例是否已经存在,若存在则返回,不存在则再建立单例。这里尤其还要注意,getInstance()方法必须是同步的,否则在多线程环境下,当线程1正新建单例时,完成赋值操作前,线程2可能判断instance为null,故线程2也将启动新建单例的程序,而导致多个实例被创建,故同步关键字是必须的。

使用上例中的单例实现,虽然实现了延迟加载的功能,但和第一种方法相比,它引入了同步关键字,因此在多线程环境中,它的时耗要远远大于第一种单例模式。以下测试代码就说明了这个问题:

@Override
public void run(){
for(int i=0;i<100000;i++)
Singleton.getInstance();
//LazySingleton.getInstance();
System.out.println("spend:"+(System.currentTimeMillis()-begintime));
}
开启5个线程同时完成以上代码的运行,使用第1种类型的单例耗时0ms,而使用LazySingleton却相对耗时约390ms。性能至少相差2个数量级。

注意:在本书中,会使用很多类似的代码片段用于测试不同代码的执行速度。在不同的计算机上其测试结果很可能与笔者不同。读者大可不必关心测试数据的绝对值,只要观察用于比较的目标代码间的相对耗时即可。

为了使用延迟加载引入的同步关键字反而降低了系统性能,是不是有点得不偿失呢?为了解决这个为问题,还需要对其进行改进:

public class StaticSingleton {
private StaticSingleton(){
System.out.println("StaticSingleton is create");
}
private static class SingletonHolder {
private static StaticSingleton instance = new StaticSingleton();
}
public static StaticSingleton getInstance() {
return SingletonHolder.instance;
}
}
在这个实现中,单例模式使用内部类来维护单例的实例,当StaticSingleton被加载时,其内部类并不会被初始化,故可以确保当StaticSingleton类被载入JVM时,不会初始化单例类,而当getInstance()方法被调用时,才会加载SingletonHolder,从而初始化instance。同时,由于实例的建立是在类加载时完成,故天生对多线程友好,getInstance()方法也不需要使用同步关键字。因此,这种实现方式同时兼备以上两种实现的优点。

注意:使用内部类的方式实现单例,既可以做到延迟加载,也不必使用同步关键字,是一种比较完善的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值