单例设计模式源码分析,常用设计模式白话文总结

什么是单例

单例模式确保某各类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能,每台计算机可以有若干个打印机,但只能有一个Printer spooler,以避免两个打印作业同时输出到打印机中,每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态

单例模式特点

1、单例类只能有一个实例。

2、单例类必须自己创建自己唯一的实例。

3、单例类必须给所有其它对象提供这一实例。

单例模式优缺点

1、单例类只有一个实例
2、共享资源,全局使用
3、节省创建时间,提高性能

单例模式的七种写法

分别是「饿汉」、「懒汉(非线程安全)」、「懒汉(线程安全)」、「双重校验锁」、「静态内部类」、「枚举」和「容器类管理]

1.饿汉式

package com.xuyu.V1;

/**
 * author:须臾
 */
public class SingletonV1 {
    /**
     * 饿汉式
     *  优点:先天线程安全,当类初始化的时候就会创建该对象
     *  缺点:如果饿汉式使用频繁,可能会影响项目启动效率
     */
    private static SingletonV1 singletonV1=new SingletonV1();

    /**
     * 将构造函数私有化,禁止初始化
     */
    private SingletonV1(){}

    public static SingletonV1 getInstance(){
        return singletonV1;
    }
    /**
     * 测试单例
     */
    public static void main(String[] args) {
        SingletonV1 instance1 = SingletonV1.getInstance();
        SingletonV1 instance2 = SingletonV1.getInstance();
        //结果为true,说明保证了单例
        System.out.println(instance1==instance2);

    }
}

源码分析Runtime

//饿汉式单例
public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }
..
}

2.懒汉式(线程不安全)

package com.xuyu.V2;

/**
 * author:须臾
 */
public class SingletonV2 {

    /**
     * 饿汉式(线程不安全)
     */
    private static SingletonV2 singletonV2;

    private SingletonV2(){}

    /**
     * 创建对象使用
     */
    public static SingletonV2 getInstance(){
        if(singletonV2==null){
            try {
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            singletonV2=new SingletonV2();
        }
        return singletonV2;
    }

    /**
     * 测试单例
     */
    public static void main(String[] args) {
        for (int i=0;i<100;i++){
            new Thread(new Runnable() {
                public void run() {
                    SingletonV2 instance1 = SingletonV2.getInstance();
                    System.out.println(Thread.currentThread().getName()+","+instance1);
                }
            }).start();
        }
    }
}

输出结果:线程不安全

Thread-1,com.xuyu.V2.SingletonV2@383a0ba
Thread-4,com.xuyu.V2.SingletonV2@d9d8ad0
Thread-0,com.xuyu.V2.SingletonV2@546431f0
Thread-5,com.xuyu.V2.SingletonV2@2858c11c
Thread-22,com.xuyu.V2.SingletonV2@3635f62a
Thread-6,com.xuyu.V2.SingletonV2@48369750
Thread-7,com.xuyu.V2.SingletonV2@2770f418
Thread-3,com.xuyu.V2.SingletonV2@6d9da26a
Thread-13,com.xuyu.V2.SingletonV2@77355386
Thread-10,com.xuyu.V2.SingletonV2@29580e2d
....
Thread-94,com.xuyu.V2.SingletonV2@3945e031
Thread-91,com.xuyu.V2.SingletonV2@5caf9db6

3.懒汉式(线程安全)

package com.xuyu.V3;

/**
 * author:须臾
 */
public class SingletonV3 {
    /**
     * 懒汉式线程安全
     */
    private static SingletonV3 singletonV3;

    private SingletonV3(){}

    /**
     * 效率低
     */
    public synchronized static SingletonV3 getInstance(){
        try {
            Thread.sleep(2000);
        }catch (Exception e){
            e.printStackTrace();
        }
        if (singletonV3==null){
            System.out.println("创建实例SingletonV3");
            singletonV3=new SingletonV3();
        }
        System.out.println("获取SingletonV3实例");
        return singletonV3;
    }
    /**
     * 测试单例
     */
    public static void main(String[] args) {
        for (int i=0;i<100;i++){
            new Thread(new Runnable() {
                public void run() {
                    SingletonV3 instance1 = SingletonV3.getInstance();
                    System.out.println(Thread.currentThread().getName()+","+instance1);
                }
            }).start();
        }
    }
}

输出结果

创建实例SingletonV3
获取SingletonV3实例
Thread-0,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
Thread-99,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
Thread-98,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
Thread-97,com.xuyu.V3.SingletonV3@95458f7
获取SingletonV3实例
....

4.双重检验锁(DCL)

package com.xuyu.V4;

public class SingletonV4 {
    /**
     * volatile 禁止指令重排序
     */
    private static volatile SingletonV4 singletonV4;

    private SingletonV4(){}

    public static SingletonV4 getInstance(){
        if(singletonV4==null){//第一次判断如果没有创建对象就开始加锁
            synchronized (SingletonV4.class){
                if (singletonV4==null){//当用户抢到锁,判断初始化
                    System.out.println("第一次开始创建实例对象,获取到锁了");
                    try {
                        Thread.sleep(2000);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    singletonV4=new SingletonV4();
                }
            }
        }
        return singletonV4;
    }
    /**
     * 测试单例
     */
    public static void main(String[] args) {
        for (int i=0;i<100;i++){
            new Thread(new Runnable() {
                public void run() {
                    SingletonV4 instance1 = SingletonV4.getInstance();
                    System.out.println(Thread.currentThread().getName()+","+instance1);
                }
            }).start();
        }
    }
}

输出结果:线程安全

第一次开始创建实例对象,获取到锁了
Thread-99,com.xuyu.V4.SingletonV4@383a0ba
Thread-89,com.xuyu.V4.SingletonV4@383a0ba
Thread-92,com.xuyu.V4.SingletonV4@383a0ba
Thread-91,com.xuyu.V4.SingletonV4@383a0ba
....
Thread-8,com.xuyu.V4.SingletonV4@383a0ba
Thread-6,com.xuyu.V4.SingletonV4@383a0ba
Thread-9,com.xuyu.V4.SingletonV4@383a0ba
Thread-12,com.xuyu.V4.SingletonV4@383a0ba
Thread-11,com.xuyu.V4.SingletonV4@383a0ba
Thread-10,com.xuyu.V4.SingletonV4@383a0ba
Thread-15,com.xuyu.V4.SingletonV4@383a0ba
Thread-19,com.xuyu.V4.SingletonV4@383a0ba
Thread-16,com.xuyu.V4.SingletonV4@383a0ba

5.静态内部内形式

package com.xuyu.V5;

/**
 * author:须臾
 */
public class SingletonV5 {
    private SingletonV5(){
        System.out.println("对象初始化...");
    }
    public static SingletonV5 getInstance(){
        return SingletonV5Utils.singletonV5;
    }
    /**
     * 静态内部类方式:能够避免同步带来的效率问题和实现懒加载
     */
    public static class SingletonV5Utils{
        private static SingletonV5 singletonV5=new SingletonV5();
    }
    /**
     * 测试单例
     */
    public static void main(String[] args) {
        System.out.println("项目启动成功。。。");
        SingletonV5 instance1 = SingletonV5.getInstance();
        SingletonV5 instance2 = SingletonV5.getInstance();
        System.out.println(instance1==instance2);

    }
}

输出结果

项目启动成功。。。
对象初始化...
true

6.枚举形式

package com.xuyu.V6;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * author:须臾
 */
public enum  EnumSingleton {
    INSTANCE;
    // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破坏
    public void add() {
        System.out.println("add方法...");
    }
    /**
     * 测试单例
     */
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;
        System.out.println(instance1==instance2);
        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        EnumSingleton v6 = declaredConstructor.newInstance();
        System.out.println(v6==instance1);

    }

}

输出结果:反射破坏不了单例

true
Exception in thread "main" java.lang.NoSuchMethodException: com.xuyu.V6.EnumSingleton.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at com.xuyu.V6.EnumSingleton.main(EnumSingleton.java:19)

7.使用容器管理

package com.xuyu.V7;

import java.util.HashMap;
import java.util.Map;

public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<String, Object>();
    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }
    public static Object getService(String key) {
        {
            return objMap.get(key);
        }
    }
}

这种使用SingletonManager 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

如何防止破坏单例

虽然单例通过私有构造函数,可以实现防止程序猿初始化对象,但是还可以通过反射和序列化技术破坏单例。

1.使用反射技术破坏单例
// 1. 使用懒汉式创建对象
SingletonV3 instance1 = SingletonV3.getInstance();
// 2. 使用Java反射技术初始化对象 执行无参构造函数
Constructor<SingletonV3> declaredConstructor = SingletonV3.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV3 instance2 = declaredConstructor.newInstance();
System.out.println(instance1 == instance2);

如何防止被反射破坏

私有构造函数

private SingletonV3() throws Exception {
    synchronized (SingletonV3.class) {
        if (singletonV3 != null) {
            throw new Exception("该对象已经初始化..");
        }
        System.out.println("执行SingletonV3无参构造函数...");
    }

}

2.使用序列化技术破坏单例

Singleton instance = Singleton.getInstance();
FileOutputStream fos = new FileOutputStream("E:\\code\\Singleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance);
oos.flush();
oos.close();

FileInputStream fis = new FileInputStream("E:\\code\\Singleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Singleton singleton2 = (Singleton) ois.readObject();
System.out.println(singleton2==instance)

//返回序列化获取对象 ,保证为单例
public Object readResolve() {
    return singletonV3;
}

枚举单例为什么不能够反射初始化

枚举底层原理

1.首先如果使用java的反射机制破坏单例,报错

73980d654f65ba7a62096d7a401a9d38631.jpg

通过该错误说明,该枚举类中无参数构造函数

2.使用java反编译技术,查看枚举类

6c4a23f3d65cff24ce9c15de2ac9154c001.jpg

从该图可以得出一个结论,枚举底层其实类。

  1. 枚举类底层原理分析

使用静态代码快方式,当静态代码快执行的时候初始化该对象,从而可以让开发者使用通过EnumSingleton.INSTANCE使用。

266a6100f11f942b251c12c6980a8208daa.jpg

  1. 在该反编译源码中,定义了一个类继承了Enum  该类是中没有无参构造函数,所以反射机制调用无参构造函数是无法初始化的。
  2. 在该类中有一个只有一个有参构造函数

2f3fc5a4561a1ad3a931b929c405b4bd22d.jpg

调用父类构造构造函数

aa380cfb4196374c8d318dbe87b0d380c01.jpg

Name 为定义调用对象名称,定义调用对象序号ordinal

使用注入有参构造函数是否可以破坏枚举呢?也不行的。

Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingleton v3 = declaredConstructor.newInstance();
System.out.println(v3 == instance1);

 

77b72012a4800dea0dc8d2e9ef0157b70e8.jpg

为什么报这个错误呢?主要原因是 java的反射初始化对象中,只要对象是是枚举是不会初始化的的。

5f67801ee1d617fa3b1d5451eb8f31d06d9.jpg

总结

到这里七中写法都介绍完了,至于选择用哪种形式的单例模式,取决于你的项目本身,是否是有复杂的并发环境,还是需要控制单例对象的资源消耗。

设计模式总结

策略模式:

官方描述(定义一系列算法,把他们封装起来,并且使它们可以相互替换)

白话文描述:有共同的抽象行为,具体不同的行为称作为不同的策略,最终可以使用Context上下文获取对应策略。

应用场景:解决多重if判断问题、聚合支付平台、第三方联合登陆、调用多个不同短信接口等。

责任链模式:

官方描述:(将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。)

白话文描述:每一个业务模块之间相互依赖比较有关联、每个关联模块称作为handler(处理器)使用上一个handler引用到下一个hanlder实现一个链表。

应用场景: 权限控制、网关权限控制、审批、风控系统等。

模版方法:

官方描述:定义一个算法结构,而将一些步骤延迟到子类实现。

白话文描述:

提前定义好整体的骨架,不同的行为让子类实现,相同的行为直接定义在抽象类中复用。

有大体共同抽象行为全部交给父类实现,不同的行为让子类实现。

应用场景:支付异步回调重构、Servlet实现

装饰模式:

官方描述:动态的给对象添加新的功能。

白话文描述:

在不改变原有对象的基础上附加功能,相比生成子类更灵活。

应用场景:IO流

代理模式:

官方描述:为其他对象提供一个代理以便控制这个对象的访问。

白话文描述:

在方法之前和之后做一些处理 实现AOP通知

应用场景:AOP、事务、日志、权限控制

观察者模式:

官方描述: 对象间的一对多的依赖关系。

白话文描述:

在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象收到通知并自动更新
其实就是发布订阅模式,发布者发布消息,订阅者获取消息,订阅了就能收到消息,没订阅就收不到消息。

应用场景: 发布订阅 事件通知、 Zookeeper、事件监听操作

门面模式:

官方描述: 对外提供一个统一的方法,来访问子系统中的一群接口。

该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用

状态模式:

官方描述: 允许一个对象在其对象内部状态改变时改变它的行为。

白话文  状态模式与策略模式本质上没有很大区别,主要根据行为决定,如果有共同抽象行为使用策略模式,没有共同行为就使用状态模式。

适配器模式:
官方描述: 将一个类的方法接口转换成客户希望的另外一个接口。

应用场景: mybatis日志收集、提供接口转换。

单例模式

官方描述:保证在一个jvm中只能有一个实例

本文出自蚂蚁课堂:http://www.mayikt.com

转载于:https://my.oschina.net/u/3995125/blog/3056454

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1) 优秀的程序应该是这样的:阅读时,感觉很优雅;新增功能时,感觉很轻松;运行时,感觉很快速,这就需要设计模式支撑。2) 设计模式包含了大量的编程思想,讲授和真正掌握并不容易,网上的设计模式课程不少,大多讲解的比较晦涩,没有真实的应用场景和框架源码支撑,学习后,只知其形,不知其神。就会造成这样结果: 知道各种设计模式,但是不知道怎么使用到真实项目。本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,比如 单例模式的8种实现方式、工厂模式的3种实现方式、适配器模式的3种实现、代理模式的3种方式、深拷贝等3) 如果你想写出规范、漂亮的程序,就花时间来学习下设计模式吧课程内容和目标本课程是使用Java来讲解设计模式,考虑到设计模式比较抽象,授课采用 图解+框架源码分析的方式1) 内容包括: 设计模式七大原则(单一职责、接口隔离、依赖倒转、里氏替换、开闭原则、迪米特法则、合成复用)、UML类图(类的依赖、泛化和实现、类的关联、聚合和组合) 23种设计模式包括:创建型模式:单例模式(8种实现)、抽象工厂模式、原型模式、建造者模式、工厂模式。结构型模式:适配器模式(3种实现)、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式(3种实现)。行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)2) 学习目标:通过学习,学员能掌握主流设计模式,规范编程风格,提高优化程序结构和效率的能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值