设计模式--单例模式

简介: 所谓类的单例模式 就是采取一定的方法保证在整个软件系统中对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法) 比如 Hibemate的SessionFactory 它充当数据存储源的代理 并负责创建Session对象 SessionFactory并不是轻量级的 一般情况下 一个 项目通常只需要一个SessionFactory就够 这样就需要用到单例模式了

介绍

所谓类的单例模式 就是采取一定的方法保证在整个软件系统中对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)


比如 Hibemate的SessionFactory 它充当数据存储源的代理 并负责创建Session对象 SessionFactory并不是轻量级的 一般情况下 一个 项目通常只需要一个SessionFactory就够 这样就需要用到单例模式了


单例模式的八种实现方式

饿汉式(静态常量)

public class HungryMan1 {

   //1 构造器私有话 外部不能直接new对象调用

   private HungryMan1(){}

   //2 本类内部创建对象实例

   private final static HungryMan1 instance = new HungryMan1();

   //3 提供一个公有的静态方法 返回实例对象

   public static HungryMan1 getInstance(){

       return instance;

   }

}

public class HungryManTest {

   public static void main(String[] args) {

       HungryMan1 instanbce = HungryMan1.getInstance();

       HungryMan1 instabce2 = HungryMan1.getInstance();

       System.out.println(instanbce == instabce2);

       System.out.println(instanbce.hashCode());

       System.out.println(instabce2.hashCode());

   }

}

测试结果

image.png



优缺点说明:

(1) 优点: 这种写法比较简单, 就是在类装载的时候就完成了实例化 避免了线程同步问题


(2) 缺点: 在类装载的时候就完车实例化 没有达到lazy loading(懒加载)的效果 如果从始至终从未使用过这个实例 则会造成内存浪费


(3) 这种方式基于classloader(类装载器)机制避免了线程同步问题 不过,instance在类装载时就实例化 在单例模式中大多数都是调用getInstance方法 但是导致类装载的原因有很多种 因此不能确定有其他的方式(或者其他的静态方法) 导致类装载 这时候初始化instance就没有达到lazy loading的效果


(4) 结论: 推荐使用,但是可能会造成内存浪费


在jdk   java.lang.Runtime#getRuntime()的源码中就是使用的饿汉式的单例模式


image.png


 


饿汉式(静态代码块)

public class HungryMan2 {

   private HungryMan2(){}

   //在静态代码块中 创建单例对象

   static {

       instance = new HungryMan2();

   }

   //2 本类内部创建对象实例

   private final static HungryMan2 instance;

   //3 提供一个公有的静态方法 返回实例对象

   public static HungryMan2 getInstance(){

       return instance;

   }

}

测试同 "饿汉式静态常量"一致


优缺点说明

(1) 这种方式和上面的方法类似 只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候就执行了静态代码块中的代码 初始化类的实例 优缺点和上面是一样的


(2) 结论:推荐使用,但是可能造成内存浪费


懒汉式(线程不安全)

public class LazyMan1 {

   private static LazyMan1 instance;

   private LazyMan1(){};

   //提供一个静态的公有方法 当使用到该方法时 才会去创建 instance

   public static LazyMan1 getInstance(){

       if(instance==null){

           instance = new LazyMan1();

       }

       //等价于 jdk8 Optional写法

//        Optional.ofNullable(instance).orElseGet(()->new LazyMan1());

       return instance;

   }

}

测试同 "饿汉式静态常量"一致


优缺点说明

(1) 解决了lazy loading的问题 但是只能在单线程下使用 如果是多线程 可能会造成线程安全问题


(2) 如果再多线程下 一个线程进入了 if(instance==null)的判断语句块 还没来的及往下执行 另一个线程也通过了这个判断语句,此时就会产生多个实例 所以在多线程环境下不可以使用这种方式


(3) 结论: 在实际开发中 不要使用这种方式


懒汉式(线程安全 同步方法)

public class LazyMan2 {

   private static LazyMan2 instance;

   private LazyMan2(){};

   //提供一个静态的公有方法 加入同步处理的代码 解决线程安全问题

   public static synchronized LazyMan2 getInstance(){

       if(instance==null){

           instance = new LazyMan2();

       }

       return instance;

   }

}

测试同 "饿汉式静态常量"一致


优缺点说明

(1) 解决了线程不安全问题


(2) 效率低 每个 线程在想获得类的实例的时候 执行getInstance()方法都要进行同步 而其实这个方法只需要执行一次实例化代码就可以了 后面的想获得该类实例 直接return就可以了 方法进行同步效率太低


(3) 结论: 在实际开发中 不推荐使用


懒汉式(线程安全 同步代码块)

public class LazyMan3 {

   private static LazyMan3 instance;

   private LazyMan3(){};

   //提供一个静态的公有方法 加入同步处理的代码 解决线程安全问题

   public static LazyMan3 getInstance(){

       if(instance==null){

           synchronized(LazyMan3.class){

               instance = new LazyMan3();

           }

       }

       return instance;

   }

}

测试同 "饿汉式静态常量"一致


优缺点说明

(1) 这种方式 本意是想对 懒汉式(线程安全 同步方法)的改进,因为同步方法效率太低 改为同步产生实例化的代码块


(2) 但是这种同步不能起到线程同步的作用 跟 懒汉式(线程不安全) 遇到的情形一致 假如一个线程进入了 if(instance==null) 判断语句块 还没来得及往下执行 另一个线程也通过了这个判断语句 这时便会产生多个实例


(3) 结论: 在实际开发中 不要用这种方式


双重检查

public class LazyMan4 {

   private static volatile LazyMan4 instance;

   private LazyMan4(){};

   //提供一个静态的公有方法 加入双重检查代码 解决线程安全问题 同时解决懒加载问题

   public static synchronized LazyMan4 getInstance(){

       if(instance==null){

           synchronized(LazyMan4.class){

               if(instance==null){

                   instance = new LazyMan4();

               }

           }

       }

       return instance;

   }

}

测试同 "饿汉式静态常量"一致


优缺点说明

(1) double-check 概念是多线程开发中常使用到的 如代码中所示进行了两次 if(instance==null)检查 这样就可以保证线程安全了


(2) 这样 实例化代码只用执行一次 后面再次访问时 判断 if(instance==null),直接return实例化对象 也避免的反复进行方法同步


(3) 线程安全 延迟加载 效率较高


(4) 结论: 在实际开发中 推荐使用


静态内部类

public class LazyMan5 {

   private LazyMan5(){};

   //书写静态内部类 该类中有一个静态属性 LazyManInstance

   private static class LasyManInstance{

       private static final LazyMan5 INSTANCE = new LazyMan5();

   }

   //提供一个静态公有方法 直接返回LazyManInstance.INSTANCE

   public static synchronized LazyMan5 getInstance(){

       return LasyManInstance.INSTANCE;

   }

}

测试同 "饿汉式静态常量"一致


优缺点说明

(1) 这种方式采用了类装载的机制来保证初始化实例时只有一个线程


(2) 静态内部类方式在LazyMan5类被装载时并不会立即实例化 而是在需要实例时 调用 getInstance方法 才会装载LazyManInstance类 从而完成LazyMan5的实例化


(3) 类的静态属性只会在第一次加载类的时候初始化 所以在这里 是jvm帮我们保证了线程的安全性 在类进行初始化时 别的线程是无法进入的


(4) 解决了线程安全问题 利用静态内部类特点实现延迟加载 效率高


(5)结论: 推荐使用


枚举

public class HungryManTest {

   public static void main(String[] args) {

       LazyMan6 in = LazyMan6.INSTANCE;

       LazyMan6 in2 = LazyMan6.INSTANCE;

       in.sayHello();

       System.out.println(in==in2);

   }

}

enum LazyMan6 {

   INSTANCE;

   public void sayHello(){

       System.out.println("hello world!");

   }

}

测试:


image.png


优缺点说明:

(1) 借助jdk1.5中添加的枚举来实现单例模式 不仅能避免多线程同步问题 而且还能防止反序列化重新创建新的对象


(2) 这种方式是Effective java作者Josh Bloch推荐的方式


(3)结论:推荐使用


单例模式的注意事项和细节说明

(1) 单例模式保证了系统内存中该类只存在一个对象 节省了系统资源 对于一些需要频繁创建销毁的对象 使用单例模式可以提高系统性能


(2) 当想实例化一个单例类的时候 必须要记住使用响应的获取对象的方法 而不是使用new


(3) 单例模式使用的场景: 需要频繁的进行创建和销毁的对象 创建对象时耗时过多或耗费资源过多(重量级对象) 但又经常用到的对象 工具类对象 频繁访问数据库文件的对象(比如数据源 session工厂等)

目录
打赏
0
0
0
0
14
分享
相关文章
【设计模式】【创建型模式】单例模式(Singleton)
一、入门 什么是单例模式? 单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。 为什么要单例模式? 节省资源 场景:某些对象创
118 15
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
69 2
设计模式-单例模式练习
单例模式是Java设计模式中的重要概念,确保一个类只有一个实例并提供全局访问点。本文详解单例模式的核心思想、实现方式及线程安全问题,包括基础实现(双重检查锁)、懒汉式与饿汉式对比,以及枚举实现的优势。通过代码示例和类图,深入探讨不同场景下的单例应用,如线程安全、防止反射攻击和序列化破坏等,展示枚举实现的简洁与可靠性。
72 0
设计模式:单例模式
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。它通过私有化构造函数、自行创建实例和静态方法(如`getInstance()`)实现。适用于数据库连接池、日志管理器等需要全局唯一对象的场景。常见的实现方式包括饿汉式、懒汉式、双重检查锁、静态内部类和枚举。线程安全问题可通过`synchronized`或双重检查锁解决,同时需防止反射和序列化破坏单例。优点是避免资源浪费,缺点是可能增加代码耦合度和测试难度。实际开发中应优先选择枚举或静态内部类,避免滥用单例,并结合依赖注入框架优化使用。
设计模式2:单例模式
单例模式是一种创建型模式,确保一个类只有一个实例,并提供全局访问点。分为懒汉式和饿汉式: - **懒汉式**:延迟加载,首次调用时创建实例,线程安全通过双重检查锁(double check locking)实现,使用`volatile`防止指令重排序。 - **饿汉式**:类加载时即创建实例,线程安全但可能浪费内存。 示例代码展示了如何使用Java实现这两种模式。
54 4
前端必须掌握的设计模式——单例模式
单例模式是一种简单的创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。适用于窗口对象、登录弹窗等场景,优点包括易于维护、访问和低消耗,但也有安全隐患、可能形成巨石对象及扩展性差等缺点。文中展示了JavaScript和TypeScript的实现方法。
160 13
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
83 2
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
107 4
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
88 1

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等