单例模式的饿汉式和懒汉式的实现以及比较它们的区别比较(Java实现)

单例模式是应用的比较多的一种设计模式,也是面试时比较喜欢问的一种,这篇文章就带你学习一边单例模式的最基本的两种实现方式吧

饿汉式

顾名思义,饿汉式实现就是在类加载时就创建好了,不必等到调用获取实例方法的时候才创建对象,调用方法时直接返回就可以了。

public class HungrySingleton {
    // 一开始就获取了实例
    private static HungrySingleton singleton = new HungrySingleton();
    // 构造方法私有,外部无法构造
    private HungrySingleton(){}
    // 公有向外提供
    public static HungrySingleton getSingleton(){
        return singleton;
    }
}

测试方法

public class Test1 {
    public static void main(String[] args) {
        HungrySingleton s1 = HungrySingleton.getSingleton();
        HungrySingleton s2 = HungrySingleton.getSingleton();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s1 == s2);
    }
}
HungrySingleton@16d3586
HungrySingleton@16d3586
true

由上面的输出结果可知确实实现了饿汉式单例模式。下面来看看懒汉式单例模式

懒汉式

这个也可以顾名思义,和饿汉式恰好相反,对象实例是在方法中才创建的。

public class LazySingleton {
    private static LazySingleton singleton = null;
    private LazySingleton(){}
    // 在方法里才实例化
    public static LazySingleton getSingleton(){
        if(singleton == null){
            singleton = new LazySingleton();
        }
        return singleton;
    }
}
LazySingleton@16d3586
LazySingleton@16d3586

从测试的代结果中也可以看出,确实是获取了同一个对象,那么这两种模式有什么区别呢
1.懒汉式默认不会实例化,要等到外部调用方法时才会,饿汉式一开始就实例化了对象
2.线程安全上,饿汉式肯定是线程安全的,因为在线程没出现之前就实例化了对象,懒汉式则是线程不安全的,因为在多线程下,如果一个线程判断完实例为null就休眠或着中断,那么另一个线程也进入方法,判断实例也为null,那么该线程就会创建实例对象,而原来的那个休眠线程恢复以后,直接就执行实例化new对象这一步,那么就会出现多个实例。 下面来通过例子验证一下

public static void main(String[] args) {
        // 饿汉式
        new Thread(()->{
            System.out.println(HungrySingleton.getSingleton());
        }).start();

        new Thread(()->{
            System.out.println(HungrySingleton.getSingleton());
        }).start();

        new Thread(()->{
            System.out.println(HungrySingleton.getSingleton());
        }).start();
    }

饿汉式我开启了三个线程去获取对象,得到的都是同一个对象

HungrySingleton@1797c69
HungrySingleton@1797c69
HungrySingleton@1797c69

懒汉式下我开启了四个线程去获取对象,结果出现了一个与其它不同的对象,说明懒汉式确实是线程不安全的

LazySingleton@1797c69
LazySingleton@1ca79ec
LazySingleton@1ca79ec
LazySingleton@1ca79ec

那么有没有方法去实现线程安全的懒汉式单例呢,首先最容易的就是加锁了,用synchronized关键字

public static synchronized LazySingleton getSingleton()
// 在get方法中添加synchronized关键字,我开了二十个线程去执行,结构得到的都是同一个对象
// 除了可以在方法头添加关键字,还可以在代码块里添加,具体的效果和这个几乎是完全一样

那么问题来了,这样是不是就解决懒汉式线程安全的问题了呢,熟悉锁机制的朋友应该知道这样会使程序的运行效率大打折扣。
3.分析上面的线程安全,接下来就是性能了,可能你已经想到了,饿汉式不需要加锁,执行效率高,懒汉式需要加锁,执行效率低
4.占用内存上,饿汉式不管你用不用到它的实例对象,他一开始就已经实例化在那里了,占据了内存空间,而懒汉式等到用的时候才实例化,不会浪费内存

那来个表格总结一下吧

 

posted @ 2021-03-16 14:15  黄进广寒  阅读(474)  评论(0编辑  收藏  举报