概念
软件设计模式,又称设计模式。是一套被反复使用、多人知晓、代码设计经验的总结。描述了软件设计过程的一些不断重复发生的问题,及该问题的解决方案。是解决特定问题的一系列套路,具有一定普遍性,可以反复使用。
设计模式分类
3类27种---创建型 || 结构性 || 行为型
创建型--单例、原型、工厂方法、抽象工厂、建造者
结构型--代理、适配器、桥接、装饰、外观、享元、组合
行为型--模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器
单例设计模式
单例模式是java最简单的设计模式,这种类型的设计模式属于创建型模式,提供了一种创建对象的最佳方式
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了一种访问其唯一的对象方式,可以直接访问,不需要实例化该类的对象
单例模式的结构
单例模式主要有单例类和访问类两种角色
单例类只能创建一个实例的类
访问类使用单例类
单例模式的两种实现方式
饿汉式:类加载就会导致该单实例对象被创建
懒汉式:类加载不会导致该实例对象被创建,而是首次使用该对象时才会创建
饿汉式
该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类对象instance。instance对象随着类的加载而创建,如果该对象足够大,而一直不使用就会造成内存浪费
public class Singleton{
private Singleton(){} //私有构造方法
private static Singleton instance = new ehs(); //在成员位置创建该类对象
public static Singleton getInstance(){ //对外提供静态方法获取对象
return instance;
}
}
懒汉式
一、线程不安全
当调用getInstance()方法获取Singleton类对象的时候才创建Singleton类对象,这样就实现了懒加载效果,但是,多线程环境会出现线程安全问题
public class Singleton{
private Singleton(){} //私有构造方法
private static Singleton instance; //在成员位置创建类对象
public static Singleton getInstance(){ //对外提供静态方法获取对象
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
二、线程安全
实现懒加载,同时解决线程安全问题,但是在getInstance()方法添加了synchronized关键字,导致该方法执行效果低
public class Singleton{
private Singleton(){} //私有构造方法
private static Singleton instance; //在成员位置创建该类对象
public static synchronized Singleton getInstance(){ //对外提供静态方法获取对象
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
三、双重检查锁(常用)
对于getInstance()方法,绝大部分操作都是线程安全的读操作,所以没必要让每个线程必须有锁才能调用方法,需要调整加锁时机。使用volatile关键字解决多线程情况下出现的空指针问题,出现问题的原因是JVM实例化对象的时候进行优化和指令重排序操作,volatile还可以保证可见性和有序性
public class Singleton{
private Singleton(){} //私有构造方法
private static volatile Singleton instance;
public static Singleton getInstance(){ //对外提供静态方法获取对象
if(instance == null){ //第一次判断,如果instance不为null,不进入抢锁,直接返回实例
synchronized(Singleton.class){
if(instance == null){ //抢到锁再次判断是否为空
instance = new Singleton();
}
}
}
return instance;
}
}
volatile关键字
第一个作用:防止jvm指令重排
第二个作用:保证变量在多线程运行的可见性
在java内存模型中,volatile关键字作用可以保证可见性或者禁止指令重排。这里是因为singleton = new Singleton(),并非是一个原子操作原子操作,事实上在JVM中上诉语句至少做了三件事:
一、给singleton分配内存空间
二、开始调用Singleton的构造函数初始化singleton
三、将singleton对象指向分配的内存空间(执行完这步singleton就不是null了)
因为存在指令重排序的优化,也就是第2步和3步的顺序是不能保证的,最终执行顺序可能是123或132 如果是132在第三步执行完后singleton就不是null了,可是第二步还没有执行,singleton对象未完成初始化,它的属性值可能不是所预期的值,假设线程2此时进入getInstance方法,由于singleton已经不是null,所以会通过第一重检查并直接返回,但其实这时singleton并没有完成初始化,使用该实例会报错
单例模式优点和缺点
优点:单例类只有一个实例,节省内存资源,对于需要频繁创建销毁的对象,使用单例模式可以提高性能,单例模式可以在系统设置全局访问点,优化和共享数据。
缺点:单例模式一般没有接口,扩展除了修改代码基本上没有途径
懒汉模式和饿汉模式的区别--面试题
懒汉模式优点便是代码中没有使用的情况下,不会加载单例类资源不会造成资源浪费,缺点也明显,加锁同步会带来程序运行效率损失
饿汉模式优缺点恰好与懒汉模式相反,如果明确知道单例对象在程序代码中用的频繁,就可以考虑使用饿汉模式