设计模式之单例模式

核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

常见应用场景:

  1. 项目中读取配置文件的类,一般也只有一个对象。没必要每次使用配置文件的时候,都new一个对象去读取;
  2. 数据库连接池的设计一般是单利模式,因为数据库的连接是很耗资源的;
  3. 在servlet中的Application也是单利模式;
  4. 在Spring中,每个Bean都是单利的,这样的优点就是Spring容器可以去管理;
  5. 在Servet编程中,每个servlet也是单例的;
  6. springMVC中的控制器也是单例的;

单例模式的优点:

  - 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象产生需要消耗较多资源时,如读取配置文件、产生其他依赖对象时,则可以通过在启动时直接创建出一个对象,然后永久驻留内存的方式来解决

  - 单例模式可以在系统设置全局的访问点,优化环共享资源的访问

常见的四种单例模式的实现方式:

  -饿汉式(线程安全,调用效率高,不能延时加载)

  -懒汉式(线程安全,调用效率不高,可延时加载)

  -静态内部类式(线程安全,调用效率高,可延时加载)

  -枚举单例(线程安全,调用效率高,不能延时加载,并且可以避免通过反射和序列化创建新对象)

选用方式:

  单例对象占用资源少,不需要延时加载

    枚举式 好于 饿汉式;

  单例对象占用资源大,需要延时加载

    静态内部类式 好于 懒汉式;

饿汉式

  实现类:

/**
 * <p>
 * SingletonTest01 单例模式测试类:饿汉式
 * <p>*/
public class SingletonTest01 {
    //类初始化装载的时候,初始化该对象(类初始化是天然的线程安全)
    private static SingletonTest01 instance = new SingletonTest01();

    // 私有的构造器
    private SingletonTest01() {

    }

    // 方法没有同步块,并发效率高
    public static SingletonTest01 getInstance() {
        return instance;
    }
}

  测试类:

public class Client {

    public static void main(String[] args) {
        SingletonTest01 s1 = SingletonTest01.getInstance();
        SingletonTest01 s2 = SingletonTest01.getInstance();
        
        System.out.println(s1);
        System.out.println(s2);
    }
}

  测试结果:

  设计模式之单例模式

  说明:

    饿汉式单例模式中,static变量会在类装载的时候初始化,此时也不会涉及多线程的访问问题,即虚拟机只会装载一次该类,肯定不会发生多线程访问问题,因此可以省略synchronized关键字。

  缺点:

    如果加载了该类,但是在程序中没有调用getInstance(),或者永远没有调用,会造成资源浪费。

懒汉式:

  测试代码:

/**
 * <p>
 * SingletonTest02 单例模式:懒汉式
 * <p>*/
public class SingletonTest02 {

    //类初始化装载的时候,不初始化该对象(延时加载,真正使用的时候再创建)
    private static SingletonTest02 instance;

    private SingletonTest02() {

    }
    //方法需同步保证线程安全,调用效率低
    public static synchronized SingletonTest02 getInstance() {
        if (instance != null) {
            return instance;
        } else {
            return new SingletonTest02();
        }
    }
}

  测试类及测试结果同上!

  说明:实现了懒加载!

  缺点:由于每次都要同步,并发效率低!

静态内部类实现方式:

  测试类:

/**
 * <p>
 * SingletonTest03 静态内部类实现单例模式
 * <p>*/
public class SingletonTest03 {

    private static class SingletonInstance {
        private static final SingletonTest03 instance = new SingletonTest03();
    }
    // 方法没有同步,调用效率高
    public static SingletonTest03 getInstance() {
        return SingletonInstance.instance;
    }
    private SingletonTest03() {

    }
}

  说明:外部类没有static属性,则不会像饿汉式立即加载对象;只有在真正调用getInstance()才会加载静态内部类。由于加载类是线程安全的,instance是static final 类型,保证内存中只有一个实例存在,而且只能被赋值一次,从而保证线程安全性;所以静态内部类实现单例模式兼备了并发高效和懒加载双重优势

枚举实现单例模式

public enum SingletonTest04 {
    //枚举元素是单例对象
    INSTANCE;
    //添加自己的需要操作
    public String SingletonOperator(){
        return "连接到数据库实例";
    }
}

  测试代码

public class Client {
    public static void main(String[] args) {
        SingletonTest04 s1 = SingletonTest04.INSTANCE;
        SingletonTest04 s2 = SingletonTest04.INSTANCE;
        System.out.println(s1==s2);
        System.out.println(s1.SingletonOperator());
    }
}

  说明:

    优点:实现简单;枚举本身就是单例模式,由JVM从根本上保证了线程安全,同时避免了反射和序列化的漏洞!

    缺点:没有懒加载