单例模式

单例模式的八种方式

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举

八种方式详解

饿汉式(静态常量)

步骤

  1. 构造函数私有化
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法

代码实现


public class BaseSingleton {
    //加载该类时,单例就会自动被创建
    private static BaseSingleton baseSingleton = new BaseSingleton();

    //构造函数设置有私有函数,禁止他人创建实例
    private BaseSingleton() {

    }

    //通过静态方法获得创建的单例
    public static BaseSingleton newInstance() {
        return baseSingleton;
    }
}

优缺点

  • 优点:写法简单,在类装载的时候就完成了实例化,避免了线程同步的问题
  • 缺点:在类装载的时候就完成了实例化,没有达到懒加载的效果,如果从始至终都没有使用过这个实例,就会造成内存的浪费

饿汉式(静态代码块)

步骤

  1. 构造函数私有化
  2. 类的内部声明静态变量
  3. 静态代码块实现对象的初始化
  4. 向外暴露一个静态的公共方法

代码实现

代码实现

public class BaseSingleton {
    //加载该类时,单例就会自动被创建
    private static BaseSingleton baseSingleton;

    static {
        baseSingleton = new BaseSingleton();
    }

    //构造函数设置有私有函数,禁止他人创建实例
    private BaseSingleton() {

    }

    //通过静态方法获得创建的单例
    public static BaseSingleton newInstance() {
        return baseSingleton;
    }
}

优缺点

  • 和静态常亮一样,写法简单,在类装载的时候就完成了实例化,避免了线程同步的问题
  • 在类装载的时候就完成了实例化,没有达到懒加载的效果,如果从始至终都没有使用过这个实例,就会造成内存的浪费

懒汉式(线程不安全)

步骤

  1. 构造函数私有化
  2. 类的内部声明静态变量
  3. 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建,存在则直接返回

代码实现

public class LazySingleton {

    //类加载时,先不自动创建实例,将单例的引用赋值为null
    private static LazySingleton lazySingleton = null;

    //构造函数私有化,防止他人创建实例
    private LazySingleton() {
    }

    //需要时调用newInstance 创建实例
    public static LazySingleton newInstance() {
        //先判断是否为空,避免重复创建
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

优缺点

  • 起到了懒加载的作用,但是只能在单线程下使用
  • 如果在多线程下,一个线程进入了if (lazySingleton == null)中,还未来得及在往下执行,线程被挂起,另一个线程也通过了这个判断语句,这个时候就会产生多个实例,所以多线程下不可以用这种方式

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

步骤

  1. 构造函数私有化
  2. 类的内部声明静态变量
  3. 向外暴露一个同步(synchronized)的静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建,存在则直接返回

代码实现

public class LazySingleton {

    //类加载时,先不自动创建实例,将单例的引用赋值为null
    private static LazySingleton lazySingleton = null;

    //构造函数私有化,防止他人创建实例
    private LazySingleton() {
    }

    //需要时调用newInstance 创建实例,方法同步synchronized
    public static synchronized LazySingleton newInstance() {
        //先判断是否为空,避免重复创建
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}


优缺点

  • 解决了线程不安全的问题
  • 效率低下,每个线程在想获得实例的时候,执行newInstance()方法都要进行同步,而这个方法只执行一次实例化代码就够了,后面想要获得该类的实例,直接return就可以,方法进行同步效率过于低下

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

步骤

  1. 构造函数私有化
  2. 类的内部声明静态变量
  3. 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建(使用同步代码块),存在则直接返回

代码实现

public class LazySingleton {
    //类加载时,先不创建对象,单例的引用先赋值未null
    private static LazySingleton LazySingleton = null;

    //构造函数私有化,防止他人创建实例
    private LazySingleton() {
    }

    public static LazySingleton newInstance2() {
        synchronized (LazySingleton.class) {
            if (LazySingleton == null) {
                LazySingleton = new LazySingleton();
            }
        }
        return LazySingleton;
    }

}

优缺点

  • 这种方式和同步方法的方式类似,只是为了减少同步的范围将同步方法缩小到具体的实例方法处,效率问题没有降低

双重检查

步骤

  1. 构造函数私有化
  2. 类的内部声明静态变量
  3. 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,存在则直接返回,不存在的话,加入锁代码,锁代码中再次判断对象是否存在,不存在的话再进行对象的实例化

代码实现

/**
 * @Classname LazySingleton
 * @Description 双重校验锁,在同步锁的基础上,添加一层if判断,若单例已经创建,则不需要执行加锁操作就可以获取实例,从而提高性能。
 * @Date 19-2-1 下午2:34
 * @Created by yujuan
 */
public class LazySingleton {
    private static LazySingleton LazySingleton;

    private LazySingleton() {
    }

    public static LazySingleton newInstance() {
        //加入双重校验
        //校验锁1 第一个if
            //作用:若单例已经创建,则直接返回创建的单例对象,无需在进行加锁操作
        if (LazySingleton == null) {
            //校验锁2 第二个if
                //作用:防止多次创建单例问题
                //原理:
                    //1、线程A调用newInstance 运行到2位置时,此时线程B也调用了newInstance
                    //2、因线程A还没有执 LazySingleton = new LazySingleton() ,此时lazySingleton为空,因此线程B能突破第一层if判断,运行到1synchronized中的A执行完成
                    //3、当线程A释放同步锁的时候,单例已经创建,LazySingleton不为空
                    //4、此时线程B从1开始位置执行到2时,此时第二层if判断不成立,不会再次创建多余的实例
            synchronized (LazySingleton.class) {
                if (LazySingleton == null) {
                    LazySingleton = new LazySingleton();
                }
            }
        }
        return LazySingleton;

    }
}

优缺点

  • 线程安全
  • 延迟加载,解决了之前单例模式懒加载效率低下的问题

静态内部类

步骤

  1. 构造函数私有化
  2. 声明一个静态内部类,在类的内部完成实例的创建
  3. 向外暴露一个静态的公共方法,提供对象的实例

代码实现

/**
 * @Classname InnerClassSingleton
 * @Description 根据静态内部类的特性,同时解决了按需加载,线程安全的问题,同时实现简单
 * 1、在静态内部类中创建单例,在装载该内部类的时候才会去创建单例
 * 2、线程安全,类是由JVM加载,而JVM只会加载一遍,保证只有一个实例
 * @Date 19-2-1 下午2:45
 * @Created by yujuan
 */
public class InnerClassSingleton {
    //创建静态内部类
    private static class InnerClassSingleton2 {
        //在静态内部类中创建实例
        private static InnerClassSingleton innerClassSingleton = new InnerClassSingleton();
    }
    //私有构造函数
    private InnerClassSingleton() {
    }
    //延迟加载,按需创建
    public static InnerClassSingleton newInstance() {
        return InnerClassSingleton2.innerClassSingleton;
    }
    //调用过程说明:
        //1、外部调用类的newInstance()
        //2、自动调用InnerClassSingleton2.innerClassSingleton 得到初始化
            //2.1 此时单例类InnerClassSingleton2 得到初始化
            //2.2 而该类在装载&被初始化时,会初始化它的静态域,从而创建单例
            //2.3 由于是静态域,因此只会JVM加载一次,java虚拟机保证的线程的安全性
        //3、最终只创建了一个单例
}

优缺点

  • 采用类装载的机制来保证初始化实例时只有一个线程
  • 静态内部类在InnerClassSingleton类被装载时不会立即实例化,而是在需要的实例化的时候,调用newInstance方法,才会进行对内部静态类的装载,从而完成对Singleton的实例化
  • 类的静态属性只会在第一次加载类的时候j进行初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的
  • 避免的线程不安全的问题,利用静态内部类特点实现延迟加载,效率高

枚举

步骤

1.创建一个枚举类,添加枚举来实现单例模式

代码实现

public enum enumSingleton {
    INSTANCE;
}

优缺点

  • 避免多线程同步的问题,可以防止反序列化重新创建新的对象

AtomicReference

使用AtomicReference 修饰对象,通过CAS的方式实现单例模式 参考阿里云开发者社区

public class Singleton {
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>(); 

    private Singleton() {}

    public static Singleton getInstance() {
        for (;;) {
            Singleton singleton = INSTANCE.get();
            if (null != singleton) {
                return singleton;
            }

            singleton = new Singleton();
            if (INSTANCE.compareAndSet(null, singleton)) {
                return singleton;
            }
        }
    }
}
posted @ 2019-11-28 11:00  晚安,Jake  阅读(182)  评论(0编辑  收藏  举报