java单例模式
单例模式的特点
- 单例类只能有一个实例;
- 单例类必须自己创建自己的唯一实例;
- 单例类必须给所有其他对象提供这一实例。
两种常用的单例类设计
懒汉式:
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null)
instance = new Singleton();
return instance;
}
}
饿汉式:
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
private static Singleton getInstance(){
return instance;
}
}
懒汉式顾名思义,在类加载的时候,并不对实例进行初始化,只有在用到对象的时候才初始化一个实例,这也叫做延迟加载(lazy loading)。饿汉式是在类加载的时候,就实例化,没有提供延迟加载的功能。懒汉式的延迟加载有利于节省内存,但多线程不安全;饿汉式避免了多线程的同步问题。下面看常用的三种线程安全的懒汉式单例模式
锁方法
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null)
instance = new Singleton();
return instance;
}
}
双重锁
public class Singleton{
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
}
静态内部类
public class Singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
第一种锁方法的实现线程安全的方式,直接将同步关键字加在方法上,这样每次调用改方法的时候都需要进行加锁,而不管有没有实例化,而加锁的很耗时,这样效率很低。PS:99%的情况下不需要同步。
第二种双重锁的方式,只有第一次进入的方法在创建实例的时候需要加锁,以后再调用该方法都不需要加锁,提高了效率。
第三种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它更像是一种提供了延迟加载的饿汉式方法。这种方式中Singleton类被装载,而instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。
单例模式用在哪些地方?
- 管理器相关的类,这种情况下管理器不能出现多个
- 封装配置文件的类,写成单例避免冲突
- 资源相关的类,如访问数据库的资源创建需要消耗较多资源
- 要求生成唯一序列号
- 需要一个共享访问点或者共享数据,比如一个计数器,可以把数值存在单例类中,而不是数据库
最佳实践
在Spring中,每个Bean默认是单例的,这样Spring容器可以管理这些Bean的生命周期。如果采用非单例模式(Prototype类型),Bean初始化后的管理交给J2EE容器,Spring容器不再跟踪管理Bean的生命周期。