设计模式2-单例模式
定义
单例模式是一种创建对象的方式,这种模式涉及到一个单一的类,该类负责创建自己的对象。同时确保只有单个对象被创建。
这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意点
1、单例类只能有一个实例
2、单例类必须自己创建自己的唯一实例
3、单例类必须给所有其他对象提供这一实例
实现方式及优缺点分析
1、懒汉单例(线程不安全式)
1 | public class Singleton{ |
优点:是懒加载的一种体现方式
缺点:这种模式线程不安全。不安全的点在于其在多线程的情况,多线程同时请求会产生多个单例,严格意义上不算是单例模式
2、懒汉单例(线程安全式)
1 | public class Singleton { |
优点:第一次调用才初始化,避免内存浪费
缺点:必须要加锁才能保证单例,但是加锁会影响效率,造成性能开销,毕竟只会出现一次实例化单例对象,其余情况都是只是获取该对象,然而每次都是经过了锁
3、饿汉单例(线程安全)
1 | public class Singleton { |
饿汉单例通过classloader机制(后续补充classloader机制文章介绍)避免了多线程的同步问题。
优点:没有加锁,执行效率变高
缺点:类加载的时候就初始化,会浪费内存,由于会有一些其他方式比如说静态方法导致类加载,因此其并非是懒加载的模式。
4、双重校验锁(线程安全)
1 | public class Singleton { |
双重校验锁实现单例,重要的是volatile防止指令重排序功能。
关键代码在于 singleton = new Singleton();这段代码内部。
这段代码事实上走了三步
1 | 1,申请内存 memory = allocate() |
这三步会进行指令重排序(后续补充指令重排序的细节),可能会变成132,132在多线程会出现问题
a线程执行了13,事实上该对象已经生成,此时b线程判断if就会是true,直接返回了这个对象,此时该对象初始化事实上没完成。就出现了问题。
因此使用volatile对singleton进行修饰,禁止指令重排序
优点:使用时实例,多线程稳定,访问也很快
缺点:略微复杂,由于指令重排序的历史限制问题,该方法仅限于jdk1.5以上版本
5,静态内部类(懒汉线程安全)
1 | public class Singleton { |
这种方式和3一样使用到了classloader机制,但是差别是当Singleton被装载的时候,singleton并不会被实例。
主要是因为sinletonHolder没有被显式调用,仅仅当singletonHolder被显式调用的时候,才会实例化singleton。
优点:能够延迟初始化静态域,线程安全
缺点:仅仅只能延迟初始化静态域,但是无法延迟实例域
6,枚举
1 | public enum Singleton { |
优点:它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
缺点:用的比较少,无法通过反射来调用初始化方法。同时也需要jdk1.5以上
如果涉及到反序列化创建对象时,可以尝试使用枚举进行初始化创建。
多进程实现单例
首先不建议这么做,多进程不适合进行单例处理。
如果真的要这么做,也是有办法的,基于多进程的进程间交互手段,开辟通信区域,实时传递。但是很蛋疼。可以实现。
参考这篇文章https://www.jianshu.com/p/2e1f7004c106。整体看下来的确可行但是没有动手的欲望。
是真的比较蛋疼。