java生成单例有哪些方法_单例模式的几种实现方式

单例模式是现如今非常普遍的模式之一。它是一种对象创建模式,用于生产一个对象的具体实例,它可以确保一个系统中一个类只产生一个实例。在java中,这样的行为带来两种好处:

1):对于频繁创建的对象,可以省略对象创建所花费的时间,对于一些重量级对象而言,是非常可观的系统开销。

2):由于new的操作减少,因而对系统内存的使用频率也会降低,这将减轻GC的压力,缩短GC的停顿时间。

因此对于系统的关键组件和被频繁使用的对象,使用单例模式可以有效改善系统性能。

下面介绍几种单例模式的写法:

1.饿汉模式

publicSingleton {privateSingleton {

System.out.println("Singleton is create")//创建单例的过程可能会比较慢

}private static Singleton instance = newSingleton();public staticSingleton getInstance() {returninstance;

}

}

这是最为普遍的一种方法,简单易懂。注意代码中重点标注的部分,首先单例必须要有一个private访问级别的构造函数。只有这样才能保证不会在系统中其他代码中实例化,这点是相当重要的;其次instance成员变量和getInstance()方法必须是static的。使用者通过Singleton.getInstance()就可以获得相关单例(类加载时创建instance对象创建于静态内存区内并且只有一个拷贝。在这里注意静态方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。而且因为实例成员与特定的对象关联,只能访问所属类的静态成员变量和成员方法。)。

不过它的不足也显而易见,就是无法对instance实例做延时加载。假如单例的创建过程很慢,而由于instance成员变量时static定义的,因此JVM加载单例类时,单例对象就会在静态内存区里建立。如果此时此单例在系统中还扮演其他角色(就是用到其他静态方法或变量),那么任何使用这个单例类的时候都会初始化这个单例变量instance,而不管是否会被用到(即任何Singleton.otherStaticMethod()时候,都会执行其构造方法输出"Singleton is create"而造成不必要的时间和内存的开销)。为了解决这个问题我们需要引入一个延迟加载机制,就是下面的懒汉模式。

2.懒汉模式

publicLazySingleton {privateLazySingleton {

System.out.println("LazySingleton is create")//创建单例的过程可能会比较慢

}private static LazySingleton instance = null;public static LazySingleton getInstanceNotSafe() {//线程不安全

if(instance = null) {

instance= newLazySingleton;

}returninstance;

}public static synchronized LazySingleton getInstanceSafe() {//线程安全

if(instance = null) {

instance= newLazySingleton;

}returninstance;

}

}

这里首先对于静态变量instance初始值赋予null,确保类加载时没有额外的负载。其次在getInstance工厂方法中,判断当前单例是否存在,若存在则返回不存在时再建立单例。注意上述中getInstanceNodSafe()方法不是同步的,故在多线程的环境下线程1正新建单例时,完成赋值操作,这时线程2判断instance为null,故线程2也将新建单例的程序,而导致多个实例被创建而导致非单例。所以在多线程的环境中使用引入同步关键字的getInstanceSafe()的方法,但是因为同步需要等待它的时耗远远大于饿汉模式。

以下测试代码说明了这个问题:

public voidrun() {for(int i = 0 ;i < 100000 ;i++) {

Singleton.getInstance();//LazySingleton.getInstanceSafe();

}

System.out.println("spent:" + (System.currentTimeMillis() -begintime));

}

开启五个线程同时完成以上代码,饿汉模式的单例耗时0ms,而使用LazySingleton却相对耗时约390ms。性能上至少相差两个数量级。为了延迟加载引入了同步关键字后降低了性能,为使这个同步方法更为有效一个双重检查锁定的模式应运而生了。

3.双重检查锁定模式

publicSingleton {privateSingleton {

System.out.println("Singleton is create")//创建单例的过程可能会比较慢

}private static Singleton instance = null;public staticSingleton getInstance() {if (instance == null) {synchronized(Singleton.class) {if(instance == null) {

instance= newSingleton();

}

}

}returninstance;

}

}

此模式先判断instance是否为null,之后才进入同步语句。当第一个线程1与第二个线程2并发进入第一个if语句后,一个线程进入synchronized块来初始化instance而另一个线程则被阻断。当第一个线程退出synchronized块时,等待着的线程2进入并判断instance是否已创建再返回instance。至此对instance进行两次检查。这也是“双重检查锁定”名称的由来。与懒汉模式相比不用每次调用getinstance()都付出同步的代价,只有第一次创建才会同步,创建之后就没用了。

4.静态内部类模式

public classStaticInnerClassSingleton {privateStaticInnerClassSingleton () {

System.out.println("StaticInnerClassSingleton is create");

}private static classSingletonHolder {private static StaticInnerClassSingleton instance = newStaticInnerClassSingleton();//只会加载一次得到单个实例

}public staticStaticInnerClassSingleton getInstance() {returnSingletonHolder.instance;

}

}

在这个实现中,单例模式内部类来维护单例的实例。当StaticInnerClassSingleton加载时,其内部类不会被初始化,故StaticInnerClassSingleton类被载入JVM时,不会初始化单例类,而当getInstance()方法被调用时,才会加载SingletonHolder,从而初始化。同时,由于实例建立是在内部类加载时完成,故天生对多线程友好。getInstance()方法也不需要使用同步关键字。因此与饿汉模式相比等待延迟加载instance,所以不用担心只想调用其他静态方法时会创建一个单例。

5.总结与漏洞修复

通常情况下,用以上方式实现单例已经可以确保在系统中只存在唯一的实例,个人比较推荐用第三或第四种方法。但仍然有例外的情况,可能导致系统生成多个实例,比如在代码中,通过反射机制,强行调用单例类的私有构造函数生成多个单例,但是我们在这里先不讨论这种极端方式。但是仍有些合法的方法可能导致多个单例的产生,如:

1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

对于第一个问题修复方法:

private staticClass getClass(String classname)throwsClassNotFoundException {

ClassLoader classLoader=Thread.currentThread().getContextClassLoader();if(classLoader == null){

classLoader= Singleton.class.getClassLoader();

}return(classLoader.loadClass(classname));

}

}

对于第二个问题的修复方法:

public class Singleton implementsjava.io.Serializable {public static Singleton instance = newSingleton();protectedSingleton() {

System.out.println("Singleton is create");

}private Object readResolve() { //这里修复,阻止生成新的实例总是返回当前对象

returninstance;

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值