单例模式Java代码实现

370 阅读3分钟

单例模式

描述:

单例模式,顾名思义,保证全局只有一个实例对象,并且提供对该对象的访问方法

特点:

  1. 类的构造方法私有化
  2. 私有静态变量
  3. 提供共有的方法,访问该实例

实现:

Ⅰ 懒汉式

线程不安全;延迟加载

package com.bookmark.designpatterns.singleton;

/**
 * @author: hj
 * @date: 2020-12-19 14:24
 * @description: 1.懒汉式,线程不安全
 **/
public class SingletonImpl1 {


    //私有变量
    private static SingletonImpl1 singletonImpl1;

    //构造方法私有化
    private SingletonImpl1() {

    }

     //公有的静态方法
     public static SingletonImpl1 getUniqueInstance() {
        if (singletonImpl1 == null) { //此处判断线程不安全,存在创建多个实例的可能
            singletonImpl1 = new  SingletonImpl1();
        }
        return singletonImpl1;
    }

}

Ⅱ 饿汉式

线程安全,但是消耗资源,未使用实例的时候就已经创建出来了

package com.bookmark.designpatterns.singleton;

/**
 * @author: hj
 * @date: 2020-12-19 15:19
 * @description: 饿汉式 - 线程安全,但是比较消耗资源,未使用该实例时就已经创建出来了
 **/
public class SingletonImpl2 {

    private static SingletonImpl2 singletonImpl2 = new SingletonImpl2();

    private SingletonImpl2() {

    }

    public static SingletonImpl2 getInstance(){
        return singletonImpl2;
    }
}

Ⅲ 懒汉式

线程安全实现方法,对获取实例的方法加入同步锁,性能较差,不推荐

package com.bookmark.designpatterns.singleton;

/**
 * @author: hj
 * @date: 2020-12-19 15:37
 * @description: 懒汉式 - 线程安全,对方法加锁
 **/
public class SingletonImpl3 {
    private static SingletonImpl3 singletonImpl3;

    private SingletonImpl3() {

    }

    public static synchronized SingletonImpl3 getInstance() { //在方法上加锁,确保线程安全,但是加入锁后,性能下降,不推荐
        if (singletonImpl3 == null) {
            singletonImpl3 = new SingletonImpl3();
        }
        return singletonImpl3;
    }

}

Ⅳ 双重检验锁

线程安全, 使用 volatile 关键字防止jvm指令重排

package com.bookmark.designpatterns.singleton;

/**
 * @author: hj
 * @date: 2020-12-19 15:40
 * @description:
 * 双重检验锁 - 线程安全
 * volatile关键字防止jvm指令重排
 **/
public class SingletonImpl4 {

    private volatile static SingletonImpl4 singletonImpl4;

    private SingletonImpl4() {

    }

    public static SingletonImpl4 getInstance() {
        if (singletonImpl4 == null) {
            synchronized (SingletonImpl4.class) {//对实例化部分的代码进行加锁,相较于直接给方法加锁,提升了效率
                if (singletonImpl4 == null) {//此处if判断是为了防止多线程进入第一个if判断条件后,导致创建多个实例
                    singletonImpl4 = new SingletonImpl4();
                }
            }
        }
        return singletonImpl4;
    }

}

Ⅴ 静态内部类实现

延时加载,线程安全

package com.bookmark.designpatterns.singleton;

/**
 * @author: hj
 * @date: 2020-12-26 14:21
 * @description: 静态内部类实现  涉及到类加载相关的问题  延时加载,线程安全
 *
 * 1. 父类被加载的时候,SingletonHolder不会被加载,此时并不会占用内存
 * 2. 当调用getInstance方法的时候,INSTANCE  才会被初始化
 * 3. 由于jvm类加载的机制,保证了该实现的线程安全
 **/
public class SingletonImpl5 {

    private SingletonImpl5() {

    }
    private static class SingletonHolder{
        private static final SingletonImpl5 INSTANCE = new SingletonImpl5();
    }

    public static SingletonImpl5 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Ⅵ 枚举实现

线程安全,可以防止反射攻击,多次序列化和反序列化后也不会创建多个实例

package com.bookmark.designpatterns.singleton;

/**
 * @author: hj
 * @date: 2020-12-26 16:07
 * @description: 枚举实现
 *   1.线程安全
 *   2.可以防止反射攻击 ,其他实现不能保证,需要在构造方法中加入防止多次实例化的代码 --- 反射攻击:通过反射 ,使用setAccessible()方法可以将私有的构造方法方位级别设置为public
 *   3.多次序列化和反序列化后,不会创建多个对象; 而其他实现需要使用transient关键字修饰所有的变量
 **/
public enum  SingletonImpl6 {
    /**
     * 实例
     */
        INSTANCE;

    private String objName;

    private String getObjName() {
        return objName;
    }

    private void setObjName(String objName) {
        this.objName = objName;
    }

    public static void main(String[] args) {
        //单例测试
        SingletonImpl6 firstSingleton  = SingletonImpl6.INSTANCE;
        firstSingleton.setObjName("first name");
        System.out.println("firstSingleton = " + firstSingleton.getObjName());

        SingletonImpl6 secondSingleton = SingletonImpl6.INSTANCE;
        secondSingleton.setObjName("second name");
        System.out.println("firstSingleton = " + firstSingleton.getObjName());
        System.out.println("secondSingleton = " + secondSingleton.getObjName());

        //反射获取实例测试
        SingletonImpl6[] enumConstants = SingletonImpl6.class.getEnumConstants();
        for (SingletonImpl6 enumConstant : enumConstants) {
            System.out.println("objName = " + enumConstant.getObjName());
        }
    }
}

总结

线程安全:2 ,3 ,4,5,6

可防止反射攻击,序列化和反序列话不会创建多个对象的只有 枚举实现