Java设计模式-单例

一、什么是单例

        单例模式指的是保证在一个jvm中,一个类只有一个实例,并提供一个全局访问点。通过私有构造函数,提供唯一获取对象实例方法(getInstance())。

         优点:共享资源,节省创建时间,提高性能。

         缺点:可能存在线程不安全问题。

二、实现单例的方式

        1>  饿汉式

public class SingletonV1 {

    /**
     * 饿汉式 
     * 优点:先天性线程是安全的,当类初始化的 就会创建该对象。 
     * 缺点:如果饿汉式使用过多,可能会影响项目启动的效率问题。
     */
    private static SingletonV1 singletonV1 = new SingletonV1();

    /**
     * 将构造函数私有化 禁止初始化
     */
    private SingletonV1() {

    }

    /**
     * 提供外部访问的获取实例方法
     */
    public static SingletonV1 getInstance() {
        return singletonV1;
    }

    public static void main(String[] args) {
        SingletonV1 instance1 = SingletonV1.getInstance();
        SingletonV1 instance2 = SingletonV1.getInstance();
        System.out.println(instance1 == instance2);

    }
}

        2>  懒汉式

public class SingletonV2 {

    /**
     *  解决了饿汉式的效率问题,但是会存在线程安全问题
     * 懒汉式 
     */
    private static SingletonV2 singletonV2;

    private SingletonV2() {

    }

    /**
     * 在真正需要创建对象的时候使用...
     *
     * @return
     */
    public static SingletonV2 getInstance() {
        if (singletonV2 == null) {
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
            }
            singletonV2 = new SingletonV2();
        }
        return singletonV2;
    }

    public static void main(String[] args) {
        // 1.模拟线程不安全
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                public void run() {
                    SingletonV2 instance1 = SingletonV2.getInstance();
                    System.out.println(Thread.currentThread().getName() + "," + instance1);
                }
            }).start();
        }
    }
}

        3>  懒汉式(线程安全)

public class SingletonV3 {
    /**
     * 懒汉式 线程安全
     */
    private static SingletonV3 singletonV3;

    private SingletonV3() {

    }

    /**
     * 能够解决线程安全问题,创建和获取实例时都上锁 ,效率非常低,所以推荐使用双重检验锁
     *
     * @return
     */
    public synchronized static SingletonV3 getInstance() {
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
        }
        if (singletonV3 == null) {
            System.out.println("创建实例SingletonV3");
            singletonV3 = new SingletonV3();
        }
        System.out.println("获取SingletonV3实例");
        return singletonV3;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                public void run() {
                    SingletonV3 instance1 = SingletonV3.getInstance();
                    System.out.println(Thread.currentThread().getName() + "," + instance1);
                }
            }).start();
        }
    }

}

            4>  双重检验锁(DCL)

public class SingletonV4 {
    /**
     * volatile 禁止重排序和 提高可见性,在懒汉式的基础上,提高了获取对象的效率
     */
    private volatile static SingletonV4 singletonV4;

    private SingletonV4() {

    }

    public static SingletonV4 getInstance() {
        if (singletonV4 == null) { // 第一次判断如果没有创建对象 开始上锁...
            synchronized (SingletonV4.class) {
                if (singletonV4 == null) { // 当用户抢到锁,判断初始化
                    System.out.println("第一次开始创建实例对象....获取锁...");
                    try {
                        Thread.sleep(2000);
                    } catch (Exception e) {
                    }
                    singletonV4 = new SingletonV4();
                }
            }
        }
        return singletonV4;//在实例化完成后,进行获取对象时没有加锁
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                public void run() {
                    SingletonV4 instance1 = SingletonV4.getInstance();
                    System.out.println(Thread.currentThread().getName() + "," + instance1);
                }
            }).start();
        }
    }
}

        5>  静态内部类

public class SingletonV5 {

    private SingletonV5() {
        System.out.println("对象初始...");
    }

    public static SingletonV5 getInstance() {
        return SingletonV5Utils.singletonV5;
    }

    /**
     * 静态内部方式能够避免同步带来的效率问题 并且能实现延迟加载
     */
    public static class SingletonV5Utils {
        private static SingletonV5 singletonV5 = new SingletonV5();
    }

    public static void main(String[] args) {
        System.out.println("项目启动成功");
        SingletonV5 instance1 = SingletonV5.getInstance();
        SingletonV5 instance2 = SingletonV5.getInstance();
        System.out.println(instance1 == instance2);
    }
}

          6>  枚举

public enum EnumSingleton {
    INSTANCE;

    // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
    public void add() {
        System.out.println("add方法...");
    }
}

public static void main(String[] args) throws Exception {
    EnumSingleton instance1 = EnumSingleton.INSTANCE;
    EnumSingleton instance2 = EnumSingleton.INSTANCE;
    System.out.println(instance1 == instance2);

    //即使使用暴力破解,依旧会报错,找不到枚举的无参构造方法
    Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor();
    declaredConstructor.setAccessible(true);
    EnumSingleton v3 = declaredConstructor.newInstance();
    System.out.println(v3==instance1);
}

总结: 对于以上的6种单例的实现,可以实现线程安全,但许多仍可以通过反射及序列换破解。在不改造jvm的情况下,枚举是最为安全的单例实现。  原因就在于 反射的newInstance()方法对枚=枚举类进行了判断,如下图所示:

 参考来自: 蚂蚁课堂

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值