单例模式(Singleton Pattern)
定义
是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
隐藏起所有的构造方法。
属于创建型模式。
适用场景
确保任何情况下都绝对只有一个实例。
比如数据库连接池、配置文件读取、缓存等。
标准示例
1. 饿汉式单例
在单例类首次加载时,就创建实例。
- 优点:执行效率高,性能高,没有锁
- 缺点:某些情况下,可能会有内存浪费(系统中单例类很多的时候,无论是否使用,加载时都会初始化到内存)
public class HungrySingleton {//静态成员变量private static final HungrySingleton INSTANCE;//代码块对成员变量赋初始值static {INSTANCE = new HungrySingleton();}//私有的构造函数private HungrySingleton(){}//全局访问方法public static HungrySingleton getInstance(){return INSTANCE;}
}
2. 懒汉式单例
被外部类调用时才会创建实例。
- 优点:节省了内存
- 缺点:线程不安全问题
public class LazySingleton {private static LazySingleton instance;private LazySingleton(){}public static LazySingleton getInstance(){if(instance == null){// 位置Ainstance = new LazySingleton();// 位置B}// 位置Creturn instance;}
}
上述代码的线程不安全体现在两种情况:
① 两个线程输出实例相同,实际上是后者覆盖了前者:
有两个线程,都执行到了位置B
,这时后者会覆盖掉前者,当线程继续到位置C
时,它们输出的确是同一个实例——把前者覆盖掉的那个实例。
② 两个线程同时进入非空判断条件中,然后按顺序返回,输出两个不同实例:
有两个线程,一个执行到了位置A
,另一个执行到了位置B
,然后位置B
的线程执行到位置C
,输出它的实例;位置A
的也随后执行到位置B
,创建了新的实例覆盖原有实例,然后执行到位置C输出该新实例。两次输出的是不同的实例。
解决懒汉式单例线程安全问题的方法一:加锁
public class LazySingleton {private static LazySingleton instance;private LazySingleton(){}//加锁解决线程安全问题public synchronized static LazySingleton getInstance(){if(instance == null){instance = new LazySingleton();}return instance;}
}
但是,这种方式会有副作用,所有的获取实例的调用,都会受到synchronized的性能影响。
解决懒汉式单例线程安全问题的方法二:双重检查
public class DoubleCheckLazySingleton {//加 volatile 是为了解决指令重排序的问题,保证instanc变量先创建private volatile static DoubleCheckLazySingleton instance;private DoubleCheckLazySingleton(){}public static DoubleCheckLazySingleton getInstance(){//第一次检查,目的是要判断是否要加锁if(instance == null){synchronized (DoubleCheckLazySingleton.class){//第二次检查,目的是判断是否要创建实例if(instance == null) {instance = new DoubleCheckLazySingleton();}}}return instance;}
}
上述代码可以解决问题,只是可读性不够高。
解决懒汉式单例线程安全问题的方法三:静态内部类
利用原理:内部类只有在调用时才会被加载。
public class StaticInnerClassLazySingleton {private StaticInnerClassLazySingleton(){}public static StaticInnerClassLazySingleton getInstance(){return LazyInnerClass.INSTANCE;}private static class LazyInnerClass{private static final StaticInnerClassLazySingleton INSTANCE = new StaticInnerClassLazySingleton();}
}
3. 注册式单例
将每一个实例都缓存到统一的容器中,使用唯一标识获取实例
public class ContainerSingleton {private ContainerSingleton(){}private static ContainerSingleton instance;private static Map<String,Object> ioc = new ConcurrentHashMap<String, Object>();public static Object getInstance(String className){Object instance = null;if(!ioc.containsKey(className)){try {instance = Class.forName(className).newInstance();ioc.put(className,instance);} catch (Exception e) {e.printStackTrace();}return instance;}else{return ioc.get(className);}}
}
以上就是 单例模式 的全部内容,感谢阅读。