超大:开胃菜-单例模式
单例模式作为一种常见的设计模式,可以在开源框架以及我们日常开发中经常看到、使用到。今天就来说说这道开胃菜有哪些做法。
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;
}
静态内部类的优点,加载外部类的时候不会立即加载内部类,只有在调用方法时初始化