创建型模式
单例模式
饿汉式
// 问题1:为什么加 final
// 问题2:如果实现了序列化接口, 还要做什么来防止反序列化破坏单例:readResolve()方法
public final class Singleton implements Serializable {
// 问题3:为什么设置为私有? 是否能防止反射创建新的实例?
private Singleton() {}
// 问题4:这样初始化是否能保证单例对象创建时的线程安全?
private static final Singleton INSTANCE = new Singleton();
// 问题5:为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由
public static Singleton getInstance() {
return INSTANCE;
}
public Object readResolve() {
return INSTANCE;
}
}
低性能 懒汉式
public final class Singleton {
private Singleton() { }
private static Singleton INSTANCE = null;
// 分析这里的线程安全, 并说明有什么缺点 ==> 无论如何,都得等待所释放
public static synchronized Singleton getInstance() {
if( INSTANCE != null ){
return INSTANCE;
}
INSTANCE = new Singleton();
return INSTANCE;
}
}
DCL 优化 懒汉式
什么是双重检查锁:
双重检查锁是一个对单例模式的优化,具体做了如下步骤:
在synchronized外边,做了一个空判断,好处如下:
- (相对于直接synchronized)避免用户态到内核态的一个消耗,即不用等锁释放,进行上下文切换
new 对象的时候需要经过如下步骤:
- 分配内存空间
- 对象初始化
- instance 指针指向内存空间
不使用 volitale修饰的话,可能会出现一个指令重排,会导致如下结果
- 如果先执行 instance 指针指向内存空间:
A线程刚执行到new对象,B线程刚判断实例是否为空(即synchronized方法前).
B线程此时拿到的是没有初始化的结果(只分配了空间,还没有调用构造方法,拿到了个半成品)
- 为什么加volitale能拿到一个正常的结果:
- volitale只能保证单个JVM指令的原子性和可见性
- 真正的作用:
- 在 new 对象前边添加 store store屏障
- 在 new 对象后边添加 store load 屏障
- 最终保证 new 对象的过程,对外不可乱序获取,因此保证了线程安全性
public final class Singleton {
private Singleton() { }
// 问题1:解释为什么要加 volatile ?
//==>避免重排序后,拿到的引用,只是分配了内存空间,但是没有来得及调用构造方法!
private static volatile Singleton INSTANCE = null;
// 问题2:对比实现3, 说出这样做的意义
// ==> 创建好后,大家都直接用就行了,不像未优化的版本,就算已经创建了,也得等待锁
public static Singleton getInstance() {
if (INSTANCE != null) return INSTANCE;
synchronized (Singleton.class) {
// 问题3:为什么还要在这里加为空判断, 之前不是判断过了吗
//==>若无:t1 t2都判断外面的实例为null,t1拿到锁创建实例后,t2又进来创建一个.
if (INSTANCE != null) { // t2
return INSTANCE;
}
INSTANCE = new Singleton();
return INSTANCE;
}
}
}
静态内部类 懒汉式
public final class Singleton {
private Singleton() { }
// 问题1:属于懒汉式还是饿汉式(懒汉式,类的创建是懒汉式,第一次加载类时,才会初始化)
private static class LazyHolder {
static final Singleton INSTANCE = new Singleton();
}
// 问题2:在创建时是否有并发问题(JVM负责的类加载是能够保证线程安全的)
public static Singleton getInstance() {
return LazyHolder.INSTANCE; //在这类调用类
}
}
工厂方法模式
建造者模式
结构型模式
适配器模式
代理模式
装饰者模式
单线程线程池返回值使用装饰器模式进行了包装