单例模式优化

一、前言:

  1. 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证并发环境下中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。
  2. 常见的单例模式:
    饿汉模式: 在程序启动时即创建对象实例。
    懒汉模式:仅当程序中使用到改对象时,才回去创建对象。

二、单例模式实例:

1. 饿汉模式,程序启动,对象实例被创建 【不推荐】:

/**
 * @Des: 饿汉模式
 */
public class SingleTest01 {

    // 静态变量系统启动就会被加载
    private static SingleTest01 singleTest = new SingleTest01();

    // 私有化构造方方
    private SingleTest01(){
        System.out.println(" init ");
    }

    // 返回对象
    public static SingleTest01 newSingleTest(){
        return singleTest;
    }

}
优点:
  1. 代码实现简单,利用类加载机制保证线程安全。
缺点:
  1. 在程序启动时,就已经完成实例化,如果对象没有使用,会造成内存浪费。
  2. 如果对象在启动时存在一些耗时操作,会影响到我们程序启动时间
2、 懒汉模式, 当程序用到该实例时去创建对象,使用 synchronized 对获取方法进行加锁,实现并发安全【不推荐】:
/**
 * @Des: 懒汉模式
 */
public class SingleTest02 {

    // 静态变量保存对象
    private static SingleTest02 singleTest = null;

    // 私有化构造方法
    private SingleTest02(){
        System.out.println(" init ");
    }

    // synchronized 修饰方法保证并发安全
    public static synchronized SingleTest02 newSingleTest() {
        if (singleTest == null){
           singleTest = new SingleTest02();
        }
        return singleTest;
    }
}

优点:
  1. 就是饿汉模式的缺点
缺点:
  1. 效率太低,加锁的细粒度太大,其实仅仅在第一次创建对象时需要加锁,实例化完成之后,获取的时候完全没有必要加锁。

三、单例模式 – 懒汉模式优化:

第一种优化方案,缩小锁粒度:
  1. 使用 volatile 保证线程可见性
  2. 对象为被实例化时,通过代码快进行加锁,双重检验保证最终结果单例。

/**
 * @Des: 懒汉模式
 */
public class SingleTest03 {

    // 静态变量保存对象, volatile 保证每个线程读取最新的数据
    private static volatile SingleTest03 singleTest = null;

    // 私有化构造方法
    private SingleTest03(){
        System.out.println(" init ");
    }

    // 获取实例对象
    public static  SingleTest03 newSingleTest() {
        if (singleTest == null){
            // singleTest 为空,表示没有初始化,将当前类加锁。
            synchronized(SingleTest03.class){
                // 双重校验,避免第一个if之后有多个线程在等待。
                if (singleTest == null){
                    singleTest = new SingleTest03();
                }
            }
        }
        return singleTest;
    }

}

第二种优化方案,使用静态内部类:
  1. 利用类加载机制,保证实例化对象时仅有一个线程,内部类仅在使用时会初始化静态属性,实现了懒加载,效率高, 和第一中优化方案差不多,代码如下:

/**
 * @Des: 懒汉模式
 */
public class SingleTest04 {

    private static class SingletonInstance{
        private final static SingleTest04 SINGLETON = new SingleTest04();
    }

    private SingleTest04(){
        System.out.println(" init ");
    }

    // 获取实例对象
    public static SingleTest04 newSingleTest() {
        return SingletonInstance.SINGLETON;
    }
}


四、目前推荐的单例的创建方式:

  1. 上面的方式虽然都实现了单例模式,各有各自的优缺点。但是他们都有一个公共的 缺点,无法防止暴力创建对象,例如: 反射、序列化、克隆。
  2. 在 《Effective Java》作者的Josh Bloch提倡我们使用枚举的方式来创建单例对象,使用非常简单。
    关于枚举的博文: https://blog.csdn.net/zhangyong01245/article/details/103322007
  3. 代码示例:

/**
 * @Des: 枚举实现单例
 */
public enum Singleton {

    // Singleton的单例对象,枚举被加载时,由JVM创建,线程安全
    INSTANCE;

    // 单例对象中的方法
    public void print(){
        System.out.println(this.hashCode());
    }
}

测试类:


public class Test {
    public static void main(String[] args) {
        for (int i =0; i<10;i++){
            new Thread(new Runnable() {
                public void run() {
                    Singleton singleton = Singleton.INSTANCE;
                    singleton.print();
                }
            }).start();
        }

    }
}

打印结果:
在这里插入图片描述

End。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章