有的情况下,我们只需要创建一个实例,以此来节省创建和销毁的消耗,以及保持该实例的稳定性(不希望被外部破坏或篡改)
此时我们可以应用单例模式来创建该实例的对象,下面列举几种方法,并且说明其各自的使用场景和优缺点:
饿汉模式
顾名思义,我很饥饿,我上来就创建该实例,不惯用不用得着,并且以后都使用该实例。示例代码:
构造器是私有的,这样就避免了被外部实例化
属性加上final属性之后,也可以避免被再次赋值,而像通过下面的静态代码块来初始化,就不能加上final修饰符来限制

懒汉模式 - 双重锁
饿汉模式比较简单,但是有的时候实例化一次资源如果消耗很大的话,我们就希望在对象被实际使用到(调用)的时候才进行实例化,我们很容易想到下面的代码实现:
看起来似乎不错,但在并发多线程场景下,进行对象实例化的代码INSTANCE = new Mgr();是有可能被同时执行到的,这样一来,就不满足单例的要求了。
怎么办呢?熟悉多线程的工程师会给这个实例化方法加上一个锁来解决这个问题,像下面这样:

接着往下想,怎么办?能不能在需要进行实例化的代码块上进行精准加锁呢?像下面这样


懒汉模式 - 静态内部类
在Effective Java这本书中,作者提到了更为简洁的时间懒汉模式的方法——通过静态内部类。我们将上述代码改造一下
懒汉模式 - 枚举属性
还有一种预发层面天然支持,就是枚举,枚举的构造器默认就是private的,因此可以很好的避免外部的篡改,另外枚举的属性只有在使用到时候,才会进行初始化,因而也可以避免使用前的资源消耗。上面的代码可以修改为以下简单的几行

总结
上面介绍了4种可行的构建单例的模式:1. 饿汉模式
2. 懒汉模式 - 双重锁
3. 懒汉模式 - 静态内部类
4. 懒汉模式 - 枚举属性
一般来讲,没有特殊的考虑,使用饿汉模式即可,简单易于理解,如果处于性能考虑,3中懒汉模式可以根据实际情况进行使用。
————————————————
笔者原文发布在CSDN,欢迎点击查看:https://blog.csdn.net/mytream/article/details/124601090
也可以关注博客(请给我一根烟的时间https://blog.csdn.net/mytream),查看更多个人心得和分享
网友评论