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