当前位置: 代码迷 >> 综合 >> 创建型模式(一)、单例设计模式-Singleton
  详细解决方案

创建型模式(一)、单例设计模式-Singleton

热度:20   发布时间:2023-11-17 12:34:15.0

目录

    • 目录
  • 常见使用场景和优点
    • 1 使用场景
    • 2 优点
  • 单例实现方式
    • 1 五种常见的实现方式
    • 2 如何选择
    • 3代码实现
      • 31饿汉式
      • 32懒汉式
      • 33双重检测锁式
      • 34静态内部类式
      • 35枚举式
  • 通过反射和反序列化破解单例模式
  • 借助CountDownLatch类多线程效率测试

1. 常见使用场景和优点

1.1 使用场景

  • 项目中的配置文件,读取配置文件的类, 一般也只有一个対象没有必要每次使用配置文件数握 , 每次new一个对象去读取
  • 数据库的连接池
  • Spring的bean配置默认是单例
  • SpringMVC和Struts,Servlet中控制层都是单例

1.2. 优点

单例只产生一个实例,减少了系统性能的开销,当一个对象产生需要比较多的资源时可以考虑使用单例设计模式。


2.单例实现方式

2.1. 五种常见的实现方式

  • 饿汉式(线程安全,调用效率高,但是不能延时加载)
  • 懒汉式(线程安全,调用效率不高,但是可以延时加载)
  • 双层检测锁机制(用于JVM底层内部模型原因,偶尔会出问题,不建议使用)
  • 静态内部类(线程安全,调率效率高,可以延时加载)
  • 枚举单例(线程安全,调用效率高,不能延时加载)

2.2. 如何选择

单例对象 占用资源少,不需要延时加载:
- 枚举式(好过饿汉式)

单例对象 占用资源大,需要延时加载:
- 静态内部类式(好过懒汉式)


2.3.代码实现

2.3.1.饿汉式

/*** 测试饿汉式单例模式*/
public class SingletonDemo1 {
    //类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!private static SingletonDemo1 instance = new SingletonDemo1();  private SingletonDemo1(){}//方法没有同步,调用效率高!public static SingletonDemo1  getInstance(){return instance;}}

2.3.2.懒汉式

/*** 测试懒汉式单例模式*/
public class SingletonDemo2 {
    //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。private static SingletonDemo2 instance;  private SingletonDemo2(){ //私有化构造器}
//方法同步,调用效率低!public static  synchronized SingletonDemo2  getInstance(){if(instance==null){instance = new SingletonDemo2();}return instance;}}

2.3.3.双重检测锁式

/*** 双重检查锁实现单例模式**/
public class SingletonDemo3 {
     private static SingletonDemo3 instance = null; public static SingletonDemo3 getInstance() { if (instance == null) { SingletonDemo3 sc; synchronized (SingletonDemo3.class) { sc = instance; if (sc == null) { synchronized (SingletonDemo3.class) { if(sc == null) { sc = new SingletonDemo3(); } } instance = sc; } } } return instance; } private SingletonDemo3() { } }

2.3.4.静态内部类式

/*** 测试静态内部类实现单例模式* 这种方式:线程安全,调用效率高,并且实现了延时加载!**/
public class SingletonDemo4 {
    private static class SingletonClassInstance {
    private static final SingletonDemo4 instance = new SingletonDemo4();}private SingletonDemo4(){}//方法没有同步,调用效率高!public static SingletonDemo4  getInstance(){return SingletonClassInstance.instance;}}

2.3.5.枚举式

/*** 测试枚举式实现单例模式(没有延时加载)*/
public enum SingletonDemo5 {//这个枚举元素,本身就是单例对象!INSTANCE;//添加自己需要的操作!public void singletonOperation(){}}

3. 通过反射和反序列化破解单例模式

/*** 测试反射和反序列化破解单例模式*/
public class Client2 {
    public static void main(String[] args) throws Exception {SingletonDemo6 s1 = SingletonDemo6.getInstance();SingletonDemo6 s2 = SingletonDemo6.getInstance();System.out.println(s1);System.out.println(s2);//通过反射的方式直接调用私有构造器
// Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.bjsxt.singleton.SingletonDemo6");
// Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
// c.setAccessible(true);
// SingletonDemo6 s3 = c.newInstance();
// SingletonDemo6 s4 = c.newInstance();
// System.out.println(s3);
// System.out.println(s4);//通过反序列化的方式构造多个对象 FileOutputStream fos = new FileOutputStream("d:/a.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(s1);oos.close();fos.close();ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));SingletonDemo6 s3 =  (SingletonDemo6) ois.readObject();System.out.println(s3);}
}
/*** 测试懒汉式单例模式(如何防止反射和反序列化漏洞)**/
public class SingletonDemo6 implements Serializable {
    //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。private static SingletonDemo6 instance;  private SingletonDemo6(){ //私有化构造器if(instance!=null){throw new RuntimeException();}}//方法同步,调用效率低!public static  synchronized SingletonDemo6  getInstance(){if(instance==null){instance = new SingletonDemo6();}return instance;}//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!private Object readResolve() throws ObjectStreamException {return instance;}}

4. 借助CountDownLatch类多线程效率测试

/*** 测试多线程环境下五种创建单例模式的效率*/
public class Client3 {
    public static void main(String[] args) throws Exception {long start = System.currentTimeMillis();int threadNum = 10;final CountDownLatch  countDownLatch = new CountDownLatch(threadNum);for(int i=0;i<threadNum;i++){new Thread(new Runnable() {@Overridepublic void run() {for(int i=0;i<1000000;i++){
// Object o = SingletonDemo4.getInstance();Object o = SingletonDemo5.INSTANCE;}countDownLatch.countDown();}}).start();}countDownLatch.await(); //main线程阻塞,直到计数器变为0,才会继续往下执行!long end = System.currentTimeMillis();System.out.println("总耗时:"+(end-start));}
}
方式 time/ms
饿汉式 22
懒汉式 636
静态内部类式 28
枚举式 32
双重检查锁式 65
  相关解决方案