vlambda博客
学习文章列表

超大:开胃菜-单例模式

单例模式作为一种常见的设计模式,可以在开源框架以及我们日常开发中经常看到、使用到。今天就来说说这道开胃菜有哪些做法。

1、饿汉模式:线程安全,但在初始化阶段就实例化了对象 如果没有用到会造成资源浪费

    private static Hungry hungry = new Hungry();

public static Hungry getInstance(){
return hungry;
}

2 、懒汉模式:在多线程访问的情况下会有不安全的情况 

    2.1 最简单的懒汉模式

    private static Lazy lazy;

public static Lazy getInstance(){
if(lazy==null){
lazy = new Lazy();
}
return lazy;
}

    2.2 用synchronized实现线程安全

    private static  Lazy lazy;

public synchronized static Lazy getInstance(){
if(lazy==null){
lazy = new Lazy();
}
return lazy;
}

对方法进行里加锁,每次只有一个进程进行访问,影响性能

    2.3 对对象进行加锁,配合volatile

    private static volatile Lazy lazy;

//配合volatile 实现双重检查机制
public static Lazy getInstance1(){
if(lazy==null){
synchronized (Lazy.class){
if(lazy == null){
lazy = new Lazy();
}
}
}
return lazy;
}

(扩展一)加volatile是为了禁止jvm初始化对象时候的重排序:

初始化对象需要经历3个步骤 

1)堆内存分配空间 

2)实例化对象属性 

3)将对象指向这块区域,2至3步骤会倒序,导致其他线程误认为已经完成实例化而报错

(扩展二)dubbo 中的各种缓存对象,例如把扩展类对象加载到缓存中时用的也是这种方式:

    public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}

再来看看Holder对象Value:

public class Holder<T> {

private volatile T value;

public void set(T value) {
this.value = value;
}

public T get() {
return value;
}

}

    2.4 静态内部类实现:

    private static class Lazyhold{
private static final Lazy lazy1 = new Lazy();

}

public static Lazy getInstance2(){
return Lazyhold.lazy1;
}

静态内部类的优点,加载外部类的时候不会立即加载内部类,只有在调用方法时初始化

    2.5 枚举实现