欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

详解单例设计模式的八种方式

程序员文章站 2022-04-12 22:10:31
饿汉式(静态常量)优点: 这种写法比较简单, 就是在类装载的时候就完成实例化。 避免了线程同步问题。缺点: 在类装载的时候就完成实例化, 没有达到 Lazy Loading 的效果。 如果从始至终从未使用过这个实例, 则会造成内存的浪费这种方式基于 classloder 机制避免了多线程的同步问题, 不过, instance 在类装载时就实例化, 在单例模式中大多数都是调用 getInstance 方法, 但是导致类装载的原因有很多种, 因此不能确定有其他的方式(或者其他的静态方法) 导致类装载,...

单例模式的细节说明和使用场景

  1. 单例模式保证了 系统内存中该类只存在一个对象, 节省了系统资源, 对于一些需要频繁创建销毁的对象, 使用单例模式可以提高系统性能
  2. 当想实例化一个单例类的时候, 必须要记住使用相应的获取对象的方法, 而不是使用 new
  3. 单例模式使用的场景: 需要频繁的进行创建和销毁的对象、 创建对象时耗时过多或耗费资源过多(即: 重量级对象), 但又经常用到的对象、 工具类对象、 频繁访问数据库或文件的对象(比如数据源、 session 工厂等)

在jdk中java.lang.Runtime 是一个典型的单例模式(饿汉式)
详解单例设计模式的八种方式

饿汉式(静态常量)

  1. 优点: 这种写法比较简单, 就是在类装载的时候就完成实例化。 避免了线程同步问题。
  2. 缺点: 在类装载的时候就完成实例化, 没有达到 Lazy Loading 的效果。 如果从始至终从未使用过这个实例, 则会造成内存的浪费
  3. 这种方式基于 classloder 机制避免了多线程的同步问题, 不过, instance 在类装载时就实例化, 在单例模式中大多数都是调用 getInstance 方法, 但是导致类装载的原因有很多种, 因此不能确定有其他的方式(或者其他的静态方法) 导致类装载, 这时候初始化 instance 就没有达到 lazy loading 的效果
  4. 结论: 这种单例模式可用, 可能造成内存浪费
package com.yc.Single01; /**
 * 饿汉式(静态常量)
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.getInstance(); Singleton instance2=Singleton.getInstance(); System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } class Singleton{ //构造器私有化  private Singleton() { } //本类中创建一个实例对象 private final static Singleton instance=new Singleton(); //提供一个公有的静态方法,返回实例对象 public static Singleton getInstance() { return instance; } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

饿汉式(静态代码块)

  1. 这种方式和上面的方式其实类似, 只不过将类实例化的过程放在了静态代码块中, 也是在类装载的时候, 就执行静态代码块中的代码, 初始化类的实例。 优缺点和上面是一样的。
  2. 结论: 这种单例模式可用, 但是可能造成内存浪费
package com.yc.Single02; /**
 * 饿汉式(静态代码块)
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.getInstance(); Singleton instance2=Singleton.getInstance(); System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } class Singleton{ private final static Singleton instance; static { //在静态代码块中,创建单例对象 instance=new Singleton(); } //构造器私有化  private Singleton() { } //提供一个公有的静态方法,返回实例对象 public static Singleton getInstance() { return instance; } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

懒汉式(线程不安全)

  1. 起到了 Lazy Loading 的效果, 但是只能在单线程下使用。
  2. 如果在多线程下, 一个线程进入了 if (instance == null)判断语句块, 还未来得及往下执行, 另一个线程也通过了这个判断语句, 这时便会产生多个实例。 所以在多线程环境下不可使用这种方式
  3. 结论: 在实际开发中, 不要使用这种方式
package com.yc.Single03; /**
 * 懒汉式(线程不安全)
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.getInstance(); Singleton instance2=Singleton.getInstance(); System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } class Singleton{ private static Singleton instance; //构造器私有化  private Singleton() { } //提供一个公有的静态方法,当使用该方法时,才创建instance public static Singleton getInstance() { if(instance==null) { instance=new Singleton(); } return instance; } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

懒汉式(线程安全)

  1. 解决了线程安全问题
  2. 效率太低了, 每个线程在想获得类的实例时候, 执行 getInstance()方法都要进行同步。 而其实这个方法只执行一次实例化代码就够了, 后面的想获得该类实例, 直接 return 就行了。 方法进行同步效率太低
  3. 结论: 在实际开发中, 不推荐使用这种方式
package com.yc.Single04; /**
 * 懒汉式(线程安全)
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.getInstance(); Singleton instance2=Singleton.getInstance(); System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } class Singleton{ private static Singleton instance; //构造器私有化  private Singleton() { } //提供一个公有的静态方法,当使用该方法时,才创建instance //synchronized 加入线程锁 public static synchronized Singleton getInstance() { if(instance==null) { instance=new Singleton(); } return instance; } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

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

1 该方式,本意是想对懒汉式(线程安全)进行改进,因为前面同步放大效率太低,改为同步产生实例化的代码块
2 但是这种同步并不能起到线程同步的作用,和第三种(懒汉式(线程不安全))的情形一致,加入一个线程进入了 if (instance == null)判断语句块 ,还未来得及往下执行,另一个线程也进入判断语句,这是会产生多个实例
3 结论: 在实际开发过程中,不能使用这种方式

package com.yc.Single05; /**
 * 懒汉式(线程安全 同步代码块)
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.getInstance(); Singleton instance2=Singleton.getInstance(); System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } class Singleton{ private static Singleton instance; //构造器私有化  private Singleton() { } //提供一个公有的静态方法,当使用该方法时,才创建instance //synchronized 加入线程锁 public static Singleton getInstance() { if(instance==null) { synchronized(Singleton.class) { instance=new Singleton(); } } return instance; } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

双重检查

  1. Double-Check 概念是多线程开发中常使用到的, 如代码中所示, 我们进行了两次 if (instance== null)检查, 这样就可以保证线程安全了。
  2. 这样, 实例化代码只用执行一次, 后面再次访问时, 判断 if (instance== null), 直接 return 实例化对象, 也避免的反复进行方法同步
  3. 线程安全; 延迟加载; 效率较高
  4. 结论: 在实际开发中, 推荐使用这种单例设计模式
package com.yc.Single06; /**
 * 双重检查
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.getInstance(); Singleton instance2=Singleton.getInstance(); System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } class Singleton{ //volatile是Java提供的一种轻量级的同步机制 private static volatile Singleton instance; //构造器私有化  private Singleton() { } //提供一个公有的静态方法,,加入双重检查,当使用该方法时,才创建instance public static Singleton getInstance() { if(instance==null) { synchronized(Singleton.class) { if(instance==null) { instance=new Singleton(); } } } return instance; } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

静态内部类

  1. 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
  2. 静态内部类方式在 Singleton 类被装载时并不会立即实例化, 而是在需要实例化时, 调用 getInstance 方法, 才会装载 SingletonInstance 类, 从而完成 Singleton 的实例化。
  3. 类的静态属性只会在第一次加载类的时候初始化, 所以在这里, JVM 帮助我们保证了线程的安全性, 在类进行初始化时, 别的线程是无法进入的。
  4. 优点: 避免了线程不安全, 利用静态内部类特点实现延迟加载, 效率高
  5. 结论: 推荐使用.
package com.yc.Single07; /**
 * 静态内部类
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.getInstance(); Singleton instance2=Singleton.getInstance(); System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } class Singleton{ //volatile是Java提供的一种轻量级的同步机制 //private static volatile Singleton instance;  //构造器私有化  private Singleton() { } //写一个静态内部类,该类中有一个静态属性 private static class SingletonInstance{ private static final Singleton instance=new Singleton(); } //提供一个公有的静态方法, public static Singleton getInstance() { return SingletonInstance.instance; } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

枚举

  1. 这借助 JDK1.5 中添加的枚举来实现单例模式。 不仅能避免多线程同步问题, 而且还能防止反序列化重新创建新的对象。
  2. 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
  3. 结论: 推荐使用
package com.yc.Single08; /**
 * 枚举
 * @author Ryan_Tang
 *
 */ public class Demo { public static void main(String[] args) { Singleton instance=Singleton.INSANCE; Singleton instance2=Singleton.INSANCE; System.out.println(instance == instance2); System.out.println("instance:"+instance.hashCode()+"\ninstance2:"+ instance2.hashCode()); } } //使用枚举完成单例 enum Singleton{ INSANCE; //属性  public void method() { } } 

控制台输出结果:

true instance:705927765 instance2:705927765 

本文地址:https://blog.csdn.net/qq_45016628/article/details/107668716

相关标签: java