定义及应用场景
单例模式指的是保证一个类仅有一个实例,并提供一个全局访问点。单例模式是创建型模式。应用场景如下:
- 连接池
- spring中的applicationContext 应用上下文
单例模式的几种写法
饿汉式单例
饿汉式单例模式在类加载的时候就初始化了,并且创建了单例对象,是线程安全的,因为它类加载的时候已经初始化了,在线程出现之前,不可能出现
//饿汉式
public class HungrySingleton{
private static final HungrySingleton lan=new HungrySingleton();
private HungrySingleton (){}
//全局访问点
public static HungrySingleton getInstance(){
return lan;
}
}
缺点:在类加载的时候就初始化对象,可能会造成内存浪费。
静态内部类单例
静态内部类能解决上述懒汉式造成的内存浪费,既能保证懒加载,又线程安全。
public class StaticInnerClassSingleton{
private static class SingletonHolder{
private static final StaticInnerClassSingleton INSTANCE=new StaticInnerClassSingleton();
}
private StaticInnerClassSingleton(){
//防止反射破坏单例
if(SingletonHolder.INSTANCE!=null){
throw new RuntimeException("不允许创建多个单例");
}
}
//全局访问点
public static final StaticInnerClassSingleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
懒汉式单例
懒汉式单例,如其名一样比较懒,只有在使用时才会初始化,下面看下简单实现
//懒汉式单例
public class LazySingleton(){
private static LazySingleton lazy=null;
private LazySingleton (){}
public static LazySingleton getInstance(){
//此处2个线程进入此方法可能会造成线程不安全
if(lazy==null){
lazy=new LazySingleton();
}
return lazy;
}
}
缺点:如果在多线程的情况下,可能有2个线程同时进入到if方法中,2个线程都在if判断时,先后进入if判断并先后返回值。
双重校验锁
出现了线程不安全的懒汉式单例,有人就会说在类的方法上添加,synchronized关键词,这样会导致锁的范围很大,这样效率很低,于是有了双重检验锁。简单实现如下:
public class DouSingleton{
private static DouSingleton singleton;
private DouSingleton(){}
public static DouSingleton getInstance(){
if(singleton==null){
synchronized(DouSingleton.class){
if(singleton==null){
singleton=new DouSingleton ();
}
}
}
return singleton;
}
}
缺点:可能会造成空指针异常,解决方式是在单例对象加volatile关键字。 若不太清楚请搜索有关java内存模型的知识,这里简单说明一下,创建对象分3步:a、开辟空间;b、初始化对象信息;c、返回对象的地址给引用。volatile是防止这3个步骤的重排序。线程1 执行了ab步骤 ,线程2判断对象==null为false直接返回了对象,导致对象为空。
单例与序列化
public class Singleton implements Serializable{
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
singleton=new Singleton ();
}
}
}
return singleton;
}
//只需重写这个方法。
private Object readResolve(){
return singleton;
}
}
枚举式单例
此方式是Effective Java作者提倡的方式,它能避免多线程同步问题,还可以防止反序列化重新创建新的对象。
public enum EnumSingleton{
INSTANCE;
EnumSingleton(){}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}