设计模式第一章——单例模式

一、设计模式分类

  在开始介绍单例模式之前,我们先了解一下设计模式的分类。宏观维度分为三⼤类:创建型、结构型、行为型。实例代码
 
  创建型:顾名思义就是给我们提供⼀种对象创建的功能,让我们只关注具体的使⽤⽽不是对象的创建过程。它包含的设计模式有单例模式、建造者模式、⼯⼚模式抽象⼯⼚模式原型模式

  结构型它是使⽤组合的⽅式将类结合起来,从⽽可以⽤它来实现新的功能。它包含的设计模式是适配器模式代理模式组合模式装饰模式⾯模式

  行为型关注的是对象的⾏为,它是把对象之间的关系进⾏梳理划分和归类。它包含的设计模式有模板⽅法模式命令模式策略模式责任链模式

二、单例模式

  单例模式(Singleton Pattern)是 Java 中最简单的设计模式之⼀,此模式保证某个类在运⾏期间,只有⼀个实例对外提供服务,⽽这个类被称为单例类。

    单例模式的特点:可以减少系统内存开⽀,减少系统性能开销,避免对资源的多重占⽤、同时操作。例如:io操作、数据库链接、网络请求。

    单例模式的缺点:扩展很困难,容易引发内存泄露,测试困难,⼀定程度上违背了单⼀职责原则,进程被杀时可能有状态不⼀致问题。

2.1 单例模式分类

2.1.1 饿汉式(线程安全,调用效率高。 但是,不能延时加载)
系统运⾏起来装载类的时候就进⾏初始化实例的操作,依赖 JVM 在类装载时就完成唯⼀对象的实例化。适用于对象占用内存,要求初始化速度快。
饿汉式三种实现方式:都依赖 JVM 在类装载时就完成唯⼀对象的实例化,基于类加载的机制,它们天⽣就是线程安全的,所以都是可⾏的,第⼆种更易于理解也⽐较常⻅。
//方式一
public class Singleton {
 public static final Singleton INSTANCE = new Singleton();
  //私有化构造
 private Singleton (){}
}
//方式二
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton (){}
    public static Singleton getInstance(){
        return INSTANCE;
    }
}
//方式三
public class Singleton {
 private static Singleton instance = null;
 static {
 instance = new Singleton();
 }
 private Singleton (){}
 public static Singleton getInstance(){
 return instance;
 }
}
 
2.1.2 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
懒汉式要使用双重校验的方式实现单例比较常用的实现方式。在声明变量时使⽤了 volatile(JDK版本需要大于1.5) 关键字来保证其线程间的可⻅性;
在同步代码块中使⽤⼆次检查,以保证其不被重复实例化 同时在调⽤getInstance()⽅法时不进⾏同步锁,效率⾼。这种实现⽅式既保证了其⾼效性,也保证了其线程安全性。
public class DCLLazy {
    private static volatile DCLLazy singleton; 

    private DCLLazy(){}

    public static DCLLazy getInstance(){
        if(singleton == null){ //第一次判断,避免不必要的同步(提高效率)
            synchronized (DCLLazy.class){  //同步
                if(singleton == null){ //第二次判断,保证线程安全
                    singleton = new DCLLazy();
                }
            }
        }
        return singleton;
    }
}
2.1.3 静态内部类(线程安全,调用效率高。 但是,可以延时加载)
根据 静态内部类 的特性,同时解决了按需加载、线程安全的问题,同时实现简洁。这种⽅式利⽤了 classloder 的机制来保证初始化 instance 时只会有⼀个。需要注意的是:虽然它的名字中有“静态”两字,但它是属于“懒汉模式”的!!这种⽅式的 Singleton 类被装载时,只要 SingletonHolder 类还没有被主动使⽤,instance 就不会被初始化。只有在显式调⽤ getInstance() ⽅法时,才会装载SingletonHolder 类,从⽽实例化对象。“静态内部类”⽅式基本上弥补了 DCL ⽅式在JDK 版本低于 1.5 时⾼并发环境失效的缺陷。此⽅式并不特别常⻅,然⽽它是所有懒加载的单例实现中适⽤范围最⼴、限制最⼩、最为推荐的⼀种。
public class StaticInnerSingleton {

    //私有构造方法
    private StaticInnerSingleton(){

    }

    //创建一个私有的静态内部类,并且再内部定义一个外部类的静态成员
    private static class SingletonHolder{
        private static final StaticInnerClassSingleton SINGLETON = new StaticInnerClassSingleton();
    }

    //提供一个public的方法获取外部类的对象
    public static final StaticInnerClassSingleton getInstance(){
        return SingletonHolder.SINGLETON;
    }
}

 

2.1.4 枚举(线程安全,调用效率高,不能延时加载。弥补序列化和反射漏洞)
特点: 满⾜单例模式所需的 创建单例、线程安全、实现简洁的需求

 

public enum EnumSingleton {
 INSTANCE;
 public EnumSingleton getInstance(){
 return INSTANCE;
 }
}

 

2.1.5 ThreadLocal(懒汉式的扩展,利于线程间的传递)
import java.util.HashMap;
import java.util.Map;
public class MapContext {
    private static final ThreadLocal<MapContext> local = new
            ThreadLocal<>();

    private Map<String,Object> data = new HashMap<>();
    public Map<String, Object> getData() {
        return getAppContext().data;
    }
    //批量存数据
    public void setData(Map<String, Object> data){
        getAppContext().data.putAll(data);
    }
    //存数据
    public void set(String key, String value) {
        getAppContext().data.put(key,value);
    }
    //取数据
    public void get(String key) {
        getAppContext().data.get(key);
    }
    //初始化的实现⽅法
    private static MapContext init(){
        MapContext context = new MapContext();
        local.set(context);
        return context;
    }
    //做延迟初始化
    public static MapContext getAppContext(){
        MapContext context = local.get();
        if (null == context) {
            context = init();
        }
            return context;
    }
        //删除实例
        public static void remove() {
            local.remove();
        }
}

从代码上看是一个懒汉式,利用ThreadLocal的特性存储唯一对象实例。ThreadLocal原理参考史上最全ThreadLocal 详解(一)

 

posted @ 2024-01-24 15:21  百思得其姐  阅读(13)  评论(0)    收藏  举报