「设计模式」- 教你手写单例模式,阿里面试100%会问到的JVM

本文主要分析单例模式常见的几种实现方式

一. 类图


单例模式使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。

二. 实现方式


2.1 饿汉式

饿汉式在类加载的时候就进行实例化,这样做的好处是线程安全;但缺点也是有的,首先在加载的时候就进行实例化,万一这个类占用的资源很大,就会非常浪费资源,毕竟它不一定在什么时候被使用,但内存是一开始就被占用了。


public class HungryManSingleton {

    private static HungryManSingleton hungryManSingleton = new HungryManSingleton();



    private HungryManSingleton() { }

    

    public static HungryManSingleton getInstance() {

        return hungryManSingleton;

    }

} 

复制代码

在main方法中验证饿汉式实现的单例模式


HungryManSingleton instance1 = HungryManSingleton.getInstance();

HungryManSingleton instance2 = HungryManSingleton.getInstance();

System.out.println("从饿汉单例获取的两个实例比较:" + instance1.equals(instance2));

复制代码

输出:

使用反射破坏饿汉式单例模式:


//使用反射获取构造方法,再将构造方法的私有性破坏,然后用这个构造方法创建一个实例

Class<HungryManSingleton> singletonClass = HungryManSingleton.class;

Constructor<HungryManSingleton> declaredConstructor = singletonClass.getDeclaredConstructor();

declaredConstructor.setAccessible(true);



HungryManSingleton instance1 = HungryManSingleton.getInstance();

HungryManSingleton instance2 = declaredConstructor.newInstance();

System.out.println("与反射获取的实例比较:" + instance2.equals(instance1));

复制代码

输出:

可以看到,他们并不是同一个对象,这意味着饿汉式单例模式被破坏了

事实上,使用反射后,无论是饿汉式、懒汉式、升级的双重校验锁机制、静态内部类机制,都是不安全的

2.2 懒汉式

在懒汉式的实现中,默认不会进行实例化,什么时候用到了,什么时候 New,从而节约资源


public class LazySingleton {

    private static LazySingleton lazySingleton;

    

    private LazySingleton() {

        System.out.println(Thread.currentThread().getName());

    }



    public static LazySingleton getInstance() {

        if (lazySingleton == null) lazySingleton = new LazySingleton();

        return lazySingleton;

    }

}

复制代码

但是这个实现在多线程的环境下是不安全的,试想以下,当 lazySingleton 为空时,试想一下,当lazySingleton 为空时,有多个线程同时通过了if (lazySingleton == null) 的判断,这样就会导致new 被执行了多次,使用代码复现一下:


public static void main(String[] args) {

    for (int i = 0; i < 10; i++) {

        new Thread(() -> LazySingleton.getInstance()).start();

    }

}

复制代码

控制台输出:

可以看到,实例化代码被执行了三次,为了解决线程安全的问题有两个方法:

  1. getInstance() 方法的层级上加关键字 synchronized

  2. 引入双重检测锁

3.3 双重校验锁

为了解决懒汉式线程不安全的问题,可以引入双重校验锁的机制,双重检验锁也是一种延迟加载,并且较好的解决了在确保线程安全的时候效率低下的问题

以下是代码实现:


public class DCLSingleton {

    private volatile static DCLSingleton dclSingleton;



    private DCLSingleton() { }



    public static DCLSingleton getInstance() {

        if (dclSingleton == null) {

            synchronized (DCLSingleton.class) {

                if (dclSingleton == null) dclSingleton = new DCLSingleton();

            }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值