学习笔记--设计模式--单例模式

 

一、单例模式-懒汉式

2019-08-05 14:09:23

单例模式

定义:保证一个类只有一个实例,并提供一个全局访问点。

关键:构造器的私有化

1、普通模式

 1 public class LazySingleton {
 2     private static LazySingleton lazySingleton = null;
 3     //私有化构造器
 4     private LazySingleton() {
 5     }
 6     public static LazySingleton getInstance() {
 7         if (lazySingleton == null) {
 8             lazySingleton = new LazySingleton();
 9         }
10         return lazySingleton;
11     }
12 }

 存在问题:线程不安全。

 解决方式:加锁

2、同步方法

public class LazySingleton {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {
    }
    //添加同步锁
    public synchronized static LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}   

 存在问题:线程锁的范围过大,性能不佳。

3、同步代码块

public class LazySingleton {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {
    }

    public  static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized (LazySingleton.class){
                lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }
}

存在问题:在多线程枪锁的过程,线程获得所得同时,其他线或许已经完成了初始化,所以可能会出现二次初始化

4、Double Check

public class LazySingleton {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {
    }

    public  static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized (LazySingleton.class){
                if(lazySingleton==null){
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }
}

LazySingleTon 的初始化过程

  1)分配内存给lazySingleton;

  2)初始化对象 new LazySingleton();

  3)设置lazySingleton指向刚刚分配的内存。

存在问题:在CPU执行代码的过程中,存在代码的二次重排,但是遵循intra-thread semantics

解决方式:

 //使用volatile实现 安全的初始化,禁止重排序。保证内存可见性,可以访问共享内存。
 private volatile static  LazyDoubleCheckSingleton singleton = null;

5、静态内部类的单例模式

   说明:

    类在使用时才会被初始化的方式:

      1)使用关键字 new 初始化类;

      2)访问类或接口中的静态变量或对静态变量赋值;

      3)调用类的静态方法;

      4)反射

        Class.forName(String className);

      5)  初始化子类,父类也会被初始化。

public class StaticInnerClassSingleton {
    private static class InnerClass {
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    private StaticInnerClassSingleton() {

    }
    
    public static StaticInnerClassSingleton getInstance() {
        //调用内部类的静态变量
        return InnerClass.staticInnerClassSingleton;
} }
二、单例模式-饿汉式
1、普通模式
public class HungrySingleton {
    //类加载时就完成初始化
    //方式一:HungrySingleton hungrySingleTon = new HungrySingleton();
    //方式二:通过静态代码块完成初始化。
    private final  static HungrySingleton  hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }

   
    private HungrySingleton(){
       
    }
    public static HungrySingleton getInstance(){
        return  hungrySingleton;
    }
}

2、保证序列化、反序列化之后仍然是同一个实例对象。

public class HungrySingleton implements Serializable {
    //类加载时就完成初始化
    //方式一:HungrySingleton hungrySingleTon = new HungrySingleton();
    //方式二:通过静态代码块完成初始化。
    private final  static HungrySingleton  hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }

    private static boolean flag =true;
    private HungrySingleton(){
       
    }
    public static HungrySingleton getInstance(){
        return  hungrySingleton;
    }
    //反序列化之后,仍然是同一个对象。
    private Object readResolve(){
        return hungrySingleton;
    }
}

3、防止反射实例化对象

public class HungrySingleton implements Serializable {
    //类加载时就完成初始化
    //方式一:HungrySingleton hungrySingleTon = new HungrySingleton();
    //方式二:通过静态代码块完成初始化。
    private final  static HungrySingleton  hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }

    private static boolean flag =true;
    private HungrySingleton(){
        //针对类加载时就完成初始化,不可反射调用。
        if(hungrySingleton!=null){
            throw  new RuntimeException("单例构造器禁止反射调用");
        }
    }
    public static HungrySingleton getInstance(){
        return  hungrySingleton;
    }

    private Object readResolve(){
        return hungrySingleton;
    }
}

三、枚举单例模式(Effective Java 推荐)

// Effective Java 推荐
public enum EnumInstance {
    INSTANCE{
        protected void test(){
            System.out.println("test");
        }
    };
    //存放数据
    private Object data;

    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static EnumInstance getInstance(){
        return INSTANCE;
    }
}

四、容器化单例模式,管理多个单例模式

//适用场景:多租户数据库获取
public class ContainerSingleton {
//    保证性能
    private static Map<String,Object> singletonMap = new HashMap<String,Object>();
//    线程安全
//    private static Map<String,Object> singletonMap = new Hashtable<>();
//    线程相对安全
//    private static Map<String,Object> singletonMap = new ConcurrentHashMap<>();
    public static void putInstance(String key,Object instance){
        if(StringUtils.isNoneBlank(key)&&instance!=null){
            //保证key的唯一性
            if(!singletonMap.containsKey(key)){
                singletonMap.put(key,instance);
            }
        }
    }

    public static Object getInstance(String key){
        return singletonMap.get(key);
    }
}

五、ThreadLocal 单例模式(借助ThreadLocal实现)

public class ThreadLocalInstance {

    private static final ThreadLocal<ThreadLocalInstance> threadlocadls = new ThreadLocal<ThreadLocalInstance>() {
        @Override
        protected ThreadLocalInstance initialValue() {
            return new ThreadLocalInstance();
        }
    };

    private ThreadLocalInstance() {
    }

    public static ThreadLocalInstance getInstance() {
        return  threadlocadls.get();
    }
}

 

posted @ 2019-08-05 10:15  BarneyWong  阅读(178)  评论(0)    收藏  举报