JAVA单例模式分析及其实现:饿汉式、懒汉式、双重检查、静态内部类
本文是对站中学院视频单例模式的学习笔记。视频在此
直入正题
单例模式是什么
首先,单例模式是设计模式的一种,是对类的编写的一种模式规范。具体到单例模式,
通俗说,例就是实例,就是对象;单就是一,代表唯一。所以说,单例模式,就是该类仅能有一个实例化对象的设计模式。
较全面的说,单例模式即使保证在整个应用程序的生命周期中的任意时刻,单例模式类的实例都只存在不超过一个(一个或者不存在)。
单例模式实现基础
知道了单例模式的基本含义,接着需要探讨实现单例类的基础,也就是怎样的设计可以实现这样的模式。
可参考如下对单例类的描述:单例模式在类中保存了它唯一的实例,同时它还提供了一个访问该唯一实例的全局方法。
在该描述中,我们获得了以下要点:
- 单例类在内部保留了它唯一的实例
- 单例类向外界提供了访问这个唯一实例的全局方法
就这两点方法,我们便可以展开分析(事实上,下文不同的写法也正是围绕着这两点进行修正。):
内部保留唯一实例
这个要点实际上需要完成两点:
1.外部不能进行实例化。
2.内部需要实例化一个私有对象
第一点,外部不能对该类进行实例化,是单例类唯一实例的根本保证。那么让外界不能对一个类进行实例化,可以采取的一种设计是让外界在进行实例化时无法调用构造方法,即将构造方法定义为私有。这样一来外部便不能在new后调用与类同名的构造方法产生实例。消除外部实例化可能性,完成!
第二点,单例类内部需要有一个实例化对象。对于这个对象,由于这个对象被类所独有,所以对象访问级别为私有;由于该类不能在外部产生对象,所以作为成员的这个对象不能是一般的成员变量,它必须为静态变量。
综上,解决这两点问题,需要
1.单例类的构造方法需要为私有
2.单例类内需要包含一个该类的静态私有变量
(对于静态内部类的写法,可将成员变量放入内部静态类中)
提供全局方法获得实例
这里,同样是由于该类不能被外部实例化对象,所以刚方法仅能通过类直接访问。所以,该方法应该是静态全局的,同时返回类型为该单例类类型
(笔者个人理解:由于涉及到线程安全和多线程下的效率问题,不同的实现方法主要就在这获得实例上斗智斗勇。)
实现单例模式
单例类的实现方法有很多。这里,仅列举六种写法:
1.饿汉式写法,静态成员变量
/*单例模式,饿汉式写法1-静态常量*/ public class Singleton1 { private static Singleton1 uniqueInstance = new Singleton1(); private Singleton1(){} public static Singleton1 getInstance() { return uniqueInstance; } }
最基本的写法,在定义静态常量同时完成实例化,获得对象方法直接返回对象。
该写法不用考虑线程的问题,但是缺点在于它在类进行加载时便进行了实例化,会造成内存浪费。
2.饿汉式写法,静态代码块
/*单例模式,饿汉式写法2-静态代码块*/ public class Singleton2 { private Singleton2(){}; private static Singleton2 uniqueInstance; static { uniqueInstance = new Singleton2(); } public static Singleton2 getInstance(){ return uniqueInstance; } }
由于装载类时,在初始化顺序上静态代码块紧随静态变量其后,所以该写法同上一个写法异曲同工,相当于将定义同初始化分开写的静态版本,本质上没有差别,故优缺点同上。
3.懒汉式写法,线程不安全型
/*单例模式,懒汉式写法,懒加载线程不安全型*/ public class Singleton3 { private Singleton3(){} private static Singleton3 uniqueInstance; public static Singleton3 getInstance() { if(uniqueInstance == null) { uniqueInstance = new Singleton3(); } return uniqueInstance; } }
为了针对上一个饿汉式写法中过早实例化占用内存的问题,可利用这种写法,在获得对象时临时判断是否初始化过对象,如果没有则现实例化一个对象。(果然如其名,有懒汉临阵磨枪的感觉)。但是这样的写法有一个致命的问题,那就是它是线程不安全的。如果有一个线程停滞在了if语句那里,在另一个线程经过if语句完成实例化后,这个线程会继续实例化一个新的对象,这样就不能保证实例唯一。
4.懒汉式写法,线程安全
/*单例模式,懒汉式写法,懒加载线程安全*/ public class Singleton4 { private Singleton4(){} private static Singleton4 uniqueInstance; public static synchronized Singleton4 getInstance() { if(uniqueInstance == null) { uniqueInstance = new Singleton4(); } return uniqueInstance; } }
针对上一个写法的线程不安全问题,可以选择在获得对象方法前添加synchronized来实现该方法的线程同步,从而保证线程安全。
但是这样的写法仍旧存在缺点,那就是在线程同步的过程中会造成不小的延迟。所以,这样的写法仍有改进的余地。
5.懒汉式写法,双重检查
/*单例模式,双重检查,懒加载高效线程安全*/ public class Singleton5 { private static volatile Singleton5 uniqueInstance; private Singleton5(){} public static Singleton5 getInstance() { if(uniqueInstance == null) { synchronized(Singleton5.class){ if(uniqueInstance == null) { uniqueInstance = new Singleton5(); } } } return uniqueInstance; } }
这里对上一个写法进行升级。为了减少线程同步带来的延迟,提高效率,将同步从对方法转为对类,加入两重判断防止重复进入,同时将对象加上volatile关键字修饰(volatile关键字作用,可参考:博文1,博文2。可以粗浅的的理解为一个轻量的synchronized,在线程中每次操作都对变量的值进行重读)。
该写法同时兼顾了线程安全和懒加载,是实际应用中的常用推荐写法。
6.懒汉式写法,静态内部类
/*单例模式,静态内部类,懒加载高效线程安全*/ public class Singleton6 { private Singleton6(){} private static class LazyHolder { private static Singleton6 uniqueInstance = new Singleton6(); } public static Singleton6 getInstance() { return LazyHolder.uniqueInstance; } }
这个写法的设计十分的巧妙,将对象定义在静态内部类中,由于内部静态类不随类加载且加载时线程安全的特性(可参考博文1、博文2)将懒加载和线程安全以及线程同步的效率问题一并解决。同时,获取实例方法也回归了最初的简易逻辑,较上一写法更为简洁,因此也作为实际使用时常用的推荐写法。
笔者水平有限,错误不妥之处,欢迎指正。
- 点赞
- 收藏
- 分享
- 文章举报
- 单例模式三种实现----饿汉式 饱汉式(懒汉式) 双重锁模式--------(java复习)
- java设计模式(一)创建型模式之 单例模式(饿汉式,懒汉式,线程安全,双重检查)
- 单例模式的懒汉式和饿汉式实现分析
- Java使用double check(双重检查)实现单例模式的一个小细节
- 单例模式的两种实现方式对比:DCL (double check idiom)双重检查 和 lazy initialization holder class(静态内部类)
- 设计模式-单例模式(饿汉式及懒汉式的Java实现)
- Java实现单例模式之饿汉式、懒汉式、枚举式,带测试。
- 理解单例设计模式(饿汉式,懒汉式,静态内部类,枚举,双重校验锁)
- Java实现单例模式之饿汉式、懒汉式、枚举式,带测试。
- Singleton 单例模式(饿汉式,懒汉式,双重锁式和内部类式),多种实现方式
- java单例设计模式详解(懒汉饿汉式)+深入分析为什么懒汉式是线程不安全的+解决办法
- 单例模式的三种实现 HungrySingleton(饿汉式) LazySingleton1(懒汉式:方法级锁) LazySingleton2(懒汉式:双检查锁 有BUG)
- Java实现单例模式之饿汉式、懒汉式、枚举式
- 单例模式中懒汉式和饿汉式 实现和总结
- java 单例模式:饿汉式与懒汉式 区别
- Twitter的分布式自增ID算法Snowflake实现分析及其Java、Php和Python版
- JAVA 单态模式(饿汉式 懒汉式)
- (10) java源码分析 ---- HashMap源码分析 及其 实现原理分析
- java中几种常用的设计模式及其实现
- java 单例模式:饿汉式与懒汉式