设计模式之单例模式
1. 概念
所谓单例模式,就是采取一定的方法使得系统中只存在某个类的一个实例,并且该类只提供一个获取对象实例的方法(静态方法);比如Hibernate的SessionFactory
2.实现
1.静态常量/静态代码块方法(饿汉式)
类加载的时候就完成了实例化,不存在线程同步问题,但是如果至始至终这个对象都用不到,那就变成了内存的浪费,没有达到Lazy Loading的效果
步骤:
-
构造器私有化(外部无法通过new来创建对象)
-
类的内部创建对象
-
向外暴露一个公共的getInstance方法
class Singleton{ //1.私有化构造器 private Singleton(){} //2.类内部创建对象 private final static Singleton instance = new Singleton(); //或者将创建对象放在静态代码块中 private static Singleton instance ; static{ instance = new Singleton(); } //3. 提供一个公共的方法获取对象 public static Singleton getInstance(){ return instance; } }
2. 懒汉式(Lazy Loading的效果)
1. 线程不安全
class Singleton{
//1.私有化构造器
private Singleton(){}
//2.类内部定义对象
private static Singleton instance;
//3. 提供一个公共的方法获取对象
public static Singleton getInstance(){
if(instance == null){//需要时再创建,不会在类加载的时候创建
instance = new Singleton();
}
return instance;
}
}
该方法只能在单线程下执行,多线程下有可能多个线程同时进入到 if 模块,以至于创建出多个实例,实际开发中不能使用这种方法
2.线程安全,同步方法
getInstance()方法中加上同步代码关键字synchronized来解决线程问题
public static synchronized Singleton getInstance(){
if(instance == null){//需要时再创建,不会在类加载的时候创建
instance = new Singleton();
}
return instance;
}
效率过低,我们在创建对象实例的时候才需要同步,而这样的代码我们在之后的每次获取实例都会进行一次同步,方法进行同步的效率太低了
3.双重检查
volatile可以使共享变量一旦修改就刷新到内存里去,在getInstance方法中,通过synchronized同步来使得线程安全,当多个线程进入外层 if 时,会受到synchronized关键字作用,单个进入到内层 if 中,当instance被创建后,由于volatile的作用会立刻刷新到内存,等待的线程就会在内层 if 中判断false,即保证了单个实例创建,再后来的线程在外层 if 就false了,也解决了效率问题
class Singleton{
//1.私有化构造器
private Singleton(){}
//2.类内部定义对象
private static volatile Singleton instance; // volatile可以使共享变量一旦修改就刷新到内存里去
//3. 提供一个公共的方法获取对象
public static Singleton getInstance(){
if(instance == null){//需要时再创建,不会在类加载的时候创建
synchronized(Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
开发中可以使用
4.静态内部类
JVM在类加载的时候是线程安全的,并且类加载的时候,其静态内部类不会被加载,所以我们可以利用这一点来达到线程安全和Lazy Loading的效果
class Singleton{
//1.对象实例在内部类加载时被创建
public static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
//2.私有化构造器
private Singleton(){}
//3. 提供一个公共的方法获取对象,调用该方法时,内部类第一次被加载,创建对象实例
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
开发中推荐使用
5.枚举
借助JDK1.5中添加的枚举来实现单例模式。不仅可以避免多线程同步问题,而且还能防止反序列化重新创建性的对象
enum Singleton{
INSTANCE;
public void play(){
//code。。。
}
}
//可以直接调用获取
Singleton instance = Singleton.INSTANCE;
//枚举中的方法也可以直接调用
instance.play();
开发中推荐使用
3.应用场景
- 需要经常创建、销毁的对象
- 创建对象耗时过多或损耗资源过多的,但又经常用到的重量级对象
- 工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等等)
分析Runtime源码中使用的单例模式