【设计模式】单例模式

前言

设计模式系列(参考资料:《Android源码设计模式解析与实战》——何红辉、关爱明)
单例模式
Builder模式
原型模式


介绍

单例可以说是使用最为广泛,也比较简单的一种设计模式了。它的使用场景主要在:当我们在整个应用当中只需要一个实例,而且在不同地方获取都返回同一个实例的时候,就可以使用单例模式去处理。例如说封装一个SharedPreference工具类,可以避免多次调用edit新建EditorImpl对象;例如数据库连接池等


定义

确保某一个类在系统运行过程中只有一个实例,而且自行实例化并向系统提供这个实例


UML类图

单例模式

Client代表高层模式,也就是我们的客户端,它属于调用方;SingleTon属于被调用方,其中的+代表public方法,-代表private方法


分类

单例模式主要分为四种实现方式,分别是:饿汉式、懒汉式 + 双重检查锁 + volatile关键字、枚举类、静态内部类。不论是哪一种实现方式,核心原理都有以下几点:

  • 1、构造函数私有化
  • 2、通过静态方法获取唯一实例
  • 3、获取过程中必须保证线程安全和尽量保证序列化安全

下面我们一个一个地分析实现方式。


1、饿汉式

	public class SingleTon1 {
		//1、声明的时候同时也初始化
	    private static SingleTon1 sInstance = new SingleTon1();
		//2、构造函数私有化
	    private SingleTon1(){
	
	    }
		//3、静态方法提供单例
	    public static SingleTon1 getInstance() {
	        return sInstance;
	    }
	}
1.1 原理分析
1.1.1、线程安全原理
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException{
        	
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                }
                if (c == null) {
                    c = findClass(name);
                }
            }
            return c;
    }

这是类加载机制中的双亲委派机制保证的,在抽象类ClassLoaderloadClass方法中,在加载类之前,先通过Class<?> c = findLoadedClass(name);检查类是否已被加载,如果没有再通过parent.loadClass(name, false)去不断调用父类的加载方法去加载类。

1.2 优缺点

饿汉式实现单例主要有以下优缺点:

优点:
  • 1、线程安全:由类加载机制保证
  • 2、第一次获取实例的效率会比懒汉式更高
  • 3、写法简单
缺点:
  • 1、不是懒加载方式,所以如果这个单例用不上的话就会导致浪费内存空间
  • 2、会被反射所破坏
  • 3、会被序列化所破坏



2、懒汉式 + 双重检查锁 + volatile关键字

	public class SingleTon2 {
	    private static volatile SingleTon2 sInstance = null;
	
	    private SingleTon2(){
	
	    }
	
	    public static SingleTon2 getInstance() {
	        if (sInstance == null){
	            synchronized (SingleTon2.class){
	                if (sInstance == null){
	                    sInstance = new SingleTon2();
	                }
	            }
	        }
	        return sInstance;
	    }
	}
2.1 原理分析

懒汉式因为是线程不安全的,并不推荐使用。所以通常我们使用懒汉式的时候都会配合volatile和双重检查锁(DCL),这里主要讲一下几个关键代码的作用:

2.1.1、volatile关键字

这个关键字的作用是确保代码的可见性、有序性,但是不能保证代码的原子性
可见性:多线程修改变量的时候都会将变量从主存读取到线程工作内存,如果线程A修改了被volatile修饰的变量,那这时候volatile是会让其他B、C、D线程中的变量失效的,强制要求B、C、D线程重新去主存取新的值;
有序性CPU为了运行效率,有了指令重排序这个机制。以新建对象为例子,A a = new A()主要有以下几步:

  • 1、在常量池看能不能找得到类元信息,如果没有,就通过双亲委派机制去加载类
  • 2、给对象分配内存空间,如果内存是规整的话使用指针碰撞,不规整则使用空闲列表,这取决于垃圾回收算法
  • 3、根据是否开启useTLAB来决定是否使用TLABTLAB就是用来在多线程分配的情景下提高效率的一种优化方法,具体原理是JVM在伊甸区为每一个线程开辟一块私有空间,然后线程分配空间的时候在对应的空间内分配,实际上还是分配在堆内存的伊甸区内,这样就不会产生线程安全问题;如果没有开启,就通过CAS方式对内存执行写入操作
  • 4、为对象属性赋初始值
  • 5、为对象设置对象头
  • 6、执行构造函数进行初始化
  • 7、将对象的内存引用地址赋值给a

因为指令重排序的存在,所以7并不一定是最后执行,也有可能会比6更先执行,如果这种情况一发生,就会出现:
A线程在执行sInstance = new SingleTon2();这行代码的时候,B线程在执行第一重检查锁if (sInstance == null),由于A线程在执行过程中出现重排序,导致sInstance并不为null,所以B直接return 一个还没初始化完全的sIntance


2.1.2、第一重检查锁if (sInstance == null)

这里主要是防止初始化完成后在getInstancesynchronized还被调用,从而影响性能


2.1.3、第二重检查锁if (sInstance == null)

这里主要防止一种情况:两个线程同时经过第一重检查锁,进入同步代码块,A线程竞争到锁,B进入等待池,在A执行完之后其实sInstance已经初始完毕了,但是如果不加这第二重判断,那B会对sInstance进行第二次实例化


2.2 优缺点

懒汉式 + 双重检查锁 + volatile关键字 实现单例主要有以下优缺点:

优点:
  • 1、线程安全:由双重检查锁和volatile关键字保证
  • 2、懒加载实例
缺点:
  • 1、如果synchronized一瞬间被多个线程所调用,直接膨胀成重量级锁的话或许会对性能有少许影响
  • 2、会被反射所破坏
  • 3、会被序列化所破坏



3、枚举类

	public enum SingleTon4 {
	    INSTANCE;
	
	    public void doSomeThing(){
	
	    }
	}

这是我最喜欢使用的一种方式,它也是《Effective Java》所力荐的一种单例实现方式。

3.1 原理分析
3.1.1 线程安全原理

它的实现原理是在反编译后会生成这样一个文件:


public final class SingleTon4 extends Enum<SingleTon4> {
      public static final SingleTon4 INSTANCE;
      ...
}

可以看到INSTANCEstatic的,也就是跟我们前面提到的饿汉式一样

3.1.2 序列化安全原理

枚举常量和其他对象序列化不同,序列化的枚举类型,只包含name,而filed值不在序列化文件里。在序列化过程中仅仅是将枚举对象的name属性输出到结果中,反序列化过程则是通过valueOf() 方法根据name查找枚举对象。因此枚举在序列化和反序列化过程中是保持同一对象的

3.1.3 反射安全原理
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
   throw new IllegalArgumentException("Cannot reflectively create enum objects");

反射在通过newInstance创建对象时,会检查该类是否枚举,如果是的话会抛出Cannot reflectively create enum objects错误,导致无法被反射


3.2 优缺点

枚举 实现单例主要有以下优缺点:

优点:
  • 1、线程安全:由类加载机制保证
  • 2、反射安全
  • 3、序列化安全
  • 4、实现超级简单!
缺点:
  • 1、无法懒加载



4、静态内部类

	public class SingleTon3 {
	
	    private SingleTon3(){
	
	    }
	
	    private static class Inner{
	        private static SingleTon3 sInstance = new SingleTon3();
	    }
	
	    public static SingleTon3 getInstance(){
	        return Inner.sInstance;
	    }
	}
4.1 原理分析
4.1.1 线程安全原理

线程安全的原因主要还是因为对于内部类Inner来说,静态变量sIntance只会被JVM加载一次

4.1.2 懒加载原理

主要利用了外部类的加载并不会导致内部类被加载这一特性


2.2 优缺点

静态内部类 实现单例主要有以下优缺点:

优点:
  • 1、线程安全:由类加载机制保证
  • 2、懒加载实例
缺点:
  • 1、会被反射所破坏
  • 2、会被序列化所破坏




总结

本文偏记录向,如果有不对的地方,还希望各种指出来呀,就这样~共勉
ps:换了一种行文风格,不再使用>去包裹文字,希望看起来结构会更舒服一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
辽B代驾管理系统对代驾订单管理、用户咨询管理、代驾订单评价管理、代驾订单投诉管理、字典管理、论坛管理、公告管理、新闻信息管理、司机管理、用户管理、管理员管理等进行集中化处理。经过前面自己查阅的网络知识,加上自己在学校课堂上学习的知识,决定开发系统选择小程序模式这种高效率的模式完成系统功能开发。这种模式让操作员基于浏览器的方式进行网站访问,采用的主流的Java语言这种面向对象的语言进行辽B代驾管理系统程序的开发,在数据库的选择上面,选择功能强大的Mysql数据库进行数据的存放操作。辽B代驾管理系统的开发让用户查看代驾订单信息变得容易,让管理员高效管理代驾订单信息。 辽B代驾管理系统具有管理员角色,用户角色,这几个操作权限。 辽B代驾管理系统针对管理员设置的功能有:添加并管理各种类型信息,管理用户账户信息,管理代驾订单信息,管理公告信息等内容。 辽B代驾管理系统针对用户设置的功能有:查看并修改个人信息,查看代驾订单信息,查看公告信息等内容。 辽B代驾管理系统针对管理员设置的功能有:添加并管理各种类型信息,管理用户账户信息,管理代驾订单信息,管理公告信息等内容。 辽B代驾管理系统针对用户设置的功能有:查看并修改个人信息,查看代驾订单信息,查看公告信息等内容。 系统登录功能是程序必不可少的功能,在登录页面必填的数据有两项,一项就是账号,另一项数据就是密码,当管理员正确填写并提交这二者数据之后,管理员就可以进入系统后台功能操作区。项目管理页面提供的功能操作有:查看代驾订单,删除代驾订单操作,新增代驾订单操作,修改代驾订单操作。公告信息管理页面提供的功能操作有:新增公告,修改公告,删除公告操作。公告类型管理页面显示所有公告类型,在此页面既可以让管理员添加新的公告信息类型,也能对已有的公告类型信息执行编辑更新,失效的公告类型信息也能让管理员快速删除。新闻管理页面,此页面提供给管理员的功能有:新增新闻,修改新闻,删除新闻。新闻类型管理页面,此页面提供给管理员的功能有:新增新闻类型,修改新闻类型,删除新闻类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值