赞
踩
单例模式的核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
单例模式的应用场景
Windows的Task Manager(任务管理器)。
Windows的Recycle Bin(回收站)。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都new一个对象去读取。
网站的计数器,一般也是采用单例模式实现,否则难以同步。
应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
Application也是单例的典型应用。(Servlet编程中会涉及到)
在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理。
在servlet编程中,每个servlet也是单例。
在spring MVC框架/struts框架中,控制器对象也是单例。
单例模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
单例模式可以在系统设置全局的访问点,优化共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
常见的单例模式(五种)
1.饿汉式:线程安全,调用频率高。不能延迟加载。
2.懒汉式:线程安全,调用效率不高。可以延迟加载。
3.双重检测锁式:由于JVM底层内部模型原因,偶尔会出现问题。不建议使用。
4.静态内部类式:线程安全,调用效率高。可以延迟加载。
5.枚举单例:线程安全,调用效率高,不能延迟加载。可以天然的防止反射和反序列化漏洞。
注:延迟加载:当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用时才会发sql语句进行查询。
单例模式----饿汉式实现:单例对象立即加载
- public class SingletonDemo01 {
-
- //类初始化时,立即加载这个对象(没有延时加载的优势)!由于加载类时,天然的是线程安全的!
- private static SingletonDemo01 instance = new SingletonDemo01();
-
- private SingletonDemo01(){
- }
-
- //方法没有同步,调用效率高!
- public static SingletonDemo01 getInstance(){
- return instance;
- }
-
- }
饿汉式单例模式代码中,static变量会在类加载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。
存在的问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费。
单例模式----懒汉式实现:单例对象延迟加载
- public class SingletonDemo02 {
-
- //类初始化时,不初始化这个对象(延迟加载,真正用的时候再创建)。
- private static SingletonDemo02 instance;
-
- //私有化构造器
- private SingletonDemo02(){
-
- }
-
- //方法同步,调用效率低!
- public static synchronized SingletonDemo02 getInstance(){
- if (instance == null){
- instance = new SingletonDemo02();
- }
- return instance;
- }
-
- }
懒加载,延迟加载,只有真正用的时候才加载。
存在的问题:虽然资源利用率高了,但是每次调用getInstance()方法都要同步,并发效率较低。
单例模式----双重检测锁实现
- public class SingletonDemo03 {
-
- private static volatile SingletonDemo03 instance = null;
-
- public static SingletonDemo03 getInstance(){
- if (instance == null){
- SingletonDemo03 sc;
- synchronized (SingletonDemo03.class){
- sc = instance;
- if (sc == null){
- synchronized (SingletonDemo03.class){
- if (sc == null){
- sc = new SingletonDemo03();
- }
- }
- instance = sc;
- }
- }
- }
- return instance;
- }
-
- private SingletonDemo03(){
-
- }
-
- }
这个模式将内容同步到下方if内部,提高了执行的效率,不必每次获取对象都进行同步,只有第一次才同步,创建了以后就没必要了。
存在的问题:由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题。不建议使用(实际工作中用不到)。
注意:在1.4以及更早版本的Java中,许多JVM对于volatile关键字的实现会导致双重检查加锁的失效。如果使用的是旧版本的jdk,就不要用这个方法来实现单例模式。
单例模式----静态内部类实现方式
- public class SingletonDemo04 {
-
- public static class SingletonClassInstance {
- private static final SingletonDemo04 instance = new SingletonDemo04();
- }
-
- public static SingletonDemo04 getInstance() {
- return SingletonClassInstance.instance;
- }
-
- private SingletonDemo04(){
-
- }
-
- }
外部类没有static属性,则不会像饿汉式那样立即加载对象。
只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性。
兼备了并发高效调用和延迟加载的优势。
静态内部类实现方式也是一种懒加载方式。使用较多。
单例模式----使用枚举实现单例模式
- public enum SingletonDemo05 {
-
- //这个枚举元素,本身就是单例对象!
- INSTANCE;
-
- //添加自己需要的操作
- public void singletonOperation(){
-
- }
-
- public static Object INSTANCE() {
- return INSTANCE;
- }
- }
使用枚举实现单例模式,实现简单。枚举本身就是单例模式。由JVM从根本上提供保障。避免通过反射和反序列化的漏洞。
存在的问题:无延迟加载。
各种单例模式的适用场景
当单例对象占用资源较少,不需要延迟加载的时候,枚举实现单例模式优于饿汉式。
当单例对象占用资源较大,需要延迟加载的时候,静态内部类的实现方式优于懒汉式。
单例模式的类图
getInstance()方法是静态的,意味着它是一个类方法,所以可以在代码的任何地方使用SingletonDemo01.getInstance()访问它。和访问全局变量一样,只是这里我们可以延迟实例化。(类图是由idea自带的工具生成的)
以上为单例模式的学习笔记,此文章为尚学堂视频的学习笔记+自己总结。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。