参考文献:《Java程序性能优化》.葛一鸣
单例模式是最基础的设计模式之一,它是一种对象创建模式,用于产生一个对象的具体实例,他可以保证系统中一个类只产生一个实例,在java语言中,这样的行为能带来两大好处:
(1)对于频繁使用的对象,能够省略对象创建的时间,对于那些重量级对象而言,是一笔非常可观的系统开销。
(2)因为new的次数减少因此对于系统内存的使用频率也会降低,这会减轻GC(垃圾回收机制)的压力,缩短GC停顿时间。
因此,对于系统的关键组件以及被频繁使用的对象,使用单例模式,可以有效改善系统性能。
单例模式的参与者非常简单,只有单例类和使用者两个:
单例模式的核心在于通过一个借口返回唯一的对象实例。
下面一个简单的例子:
package com.example.demo;
/**
* @author 作者SMF:
* @version 创建时间:2018年9月23日 下午7:51:19
* 类说明
*/
public class Single {
private Single(){
System.out.print("单例模式");
}
private static Single instance = new Single();
public static Single getInstance(){
return instance;
}
}
注意:单例类必须要有一个private级访问的构造函数,只有这样才能保证单例类不会再系统中的其它代码内被实例化,这点是很重要的,其次instance成员变量和gerInstance方法必须是static的。
这种实现方式非常简单,而且十分可靠,唯一的不足之处是无法对instance做延时加载。假如单例的创建过程很慢,而由于instance成员变量是static定义的,JVM在加载单例类的时候,单例对象也会被创建,如果此时该单例类还在系统中扮演其他角色,那么任何使用这个单例类的地方都会初始化这个单例变量,而不管是否会用到,比如单例类作为String工厂用于创建一些字符串(该单例类及用于创建Single对象,又用于创建Sting对象:)
package com.example.demo;
/**
* @author 作者SMF:
* @version 创建时间:2018年9月23日 下午7:51:19
* 类说明
*/
public class Single {
//构造方法
private Single(){
System.out.println("单例模式");
}
//内部静态变量
private static Single instance = new Single();
//内部静态方法
public static Single getInstance(){
return instance;
}
//内部静态方法
public static void CreateString(){
System.out.println("创建String");
}
}
在直接执行Single.CreateString()方法的时候会输出:
可以看到虽然没有使用单例对象,但是他还是被创建出来了,解决方法是引入延时加载机制:
package com.example.demo;
/**
* @author 作者SMF:
* @version 创建时间:2018年9月25日 下午12:22:45
* 类说明
*/
public class LazySingleton {
private LazySingleton(){
System.out.println("单例创建");
}
private static LazySingleton instance = null;
//懒加载机制
public static synchronized LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
但是上述代码中的懒加载加入了Synchronized关键字,同步的操作,因此在线程比较多的情况下,性能会降低许多,可以通过内部类的方式进一步优化:
package com.example.demo;
/**
* @author 作者SMF:
* @version 创建时间:2018年9月25日 下午1:34:03
* 类说明
*/
public class StaticSingleton {
//使用内部类维护单例实例
private static class Singleholder{
private static StaticSingleton instance = new StaticSingleton();
}
//获取实例对象
public static StaticSingleton getInstance(){
System.out.println("获取实例对象");
return Singleholder.instance;
}
public static void createString(){
System.out.println("创建String");
}
}
这样单独使用CreateString()方法是不会加载单例类,只有使用了getInstance()方法的时候才创建单例类,而且多线程使用也不会造成大量的性能损耗。
另外还有特殊的序列化和反序列化的情况下会破坏单例,从而使系统中出现两个单例类的实例,那么解决方法是在单例类中增加一个 私有的readResolve()函数,可以防止生成新的单例对象,那么就是一个比较完善的单例对象了。
private Object readResolve(){
return instance;
}