今天看了Java程序性能优化,书中对单例模式进行了详细分析,现在做个总结。
1.概念
单例模式,顾名思义就是说该单例类为某个具体的类只提供一个实例。比如说Singleton是一个单例类,那么其为A类提供的实例与为B类提供的实例就不一样,具体各位可以做下测试。这也是当初我没理解单例模式的地方。
2.单例模式的两大好处
1)对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。
2)由于new操作的此时减少,因而对系统内存的使用频率也会降低,这将减轻gc压力,缩短gc停顿时间。
3.单例模式角色
角色 | 作用 |
单例类 | 提供单例工厂,返回单例 |
使用者 | 获取并使用单例类 |
2.单例模式的几种写法
1)第一种
public class Singleton
{
private static Singleton instance = new Singleton();
private Singleton()
{
System.out.println("Singleton is create");
}
public static Singleton getInstance()
{
return instance;
}
/**
public static void createString()
{
System.out.println("createString in Singleton");
}
*/
}
这是非常简单的单例模式实现,唯一不足的是无法对instance做延迟加载。试想一下,假如单例对象的创建过程很慢,此时我们只需调用单例类中的createString()方法,该方法是static的,所以只需使用[类名.方法名]调用即可,用不到单例类的实例instance,但是当我们调用createString()方法时,instance也被创建里,即JVM加载该单例类时,instance马上就会被初始化,这无形中增加了系统的负担。解决这个问题的方法就是引入延迟加载机制,看单例模式的第二种。
2)第二种
public class LazySingleton
{
private LazySingleton()
{
System.out.println("LazySingleton is create");
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance()
{
if(null == instance)
{
instance = new LazySingleton();
}
return instance;
}
}
这种方式首先将静态成员变量instance初始值赋予null,确保JVM加载单例类时不会初始化instance;其次,在getInstance()方法中,判断当前instance是否为空,若为空则创建单例,否则返回以存在的单例。这保证了当需要使用instance时才实例化它,这是与第一种的区别----延迟加载。
但是记住getInstance()方法必须是同步的,若不是同步的在多线程情况下很有可能导致多个实例被创建,故该方法必须是同步的。这种方式虽然实现了延迟加载,但是和第一种在多线程的环境中相比,它的耗时要远远大于第一种。解决方法看第三种。
3)第三种
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被加载时,其内部类不会被初始化,完成延迟加载的功能;而当getInstance()方法被调用时,才会加载SingletonHolder,从而初始化instance。同时,由于实例的建立是在类加载时完成,故天生对多线程友好,getInstance()方法也不需要使用同步关键字。因此,这种实现方式同时兼备以上两种实现的优点。