单例模式的N种写法
1.前言
写完这个题目,我感觉自己好像"孔乙己"啊,回字的四种写法要不要学啊~
我们经常会用到单例模式,但是我对他一直没有一个统一的的认识,比如我清楚好多种单例的写法,但是每一种是怎么演化来的?具体解决了什么问题?这块就没有那么清晰了,因此此文对单例模式进行一个总结,同时手撸一下代码加深理解.
2.介绍
单例模式,即某一个类在整个系统中有且仅有一个实例.
经常用来读取配置,获取连接等等.
3.实现思路
1.构造方法私有化.
2.提供静态的方法,返回唯一实例.
这块很好理解,要想保证只有唯一实例,构造方法就不能被别人调用,只能自己调用用来创建唯一的实例,同时,将构造方法私有化了,就需要对外提供一个访问点,以方便其他类获取这个实例.
4.具体实现
4.1 饿汉式
这种写法的优势就是,真的简单,基本就是的实现思路的耿直实现,代码如下:
public class HungrySingleton { private static HungrySingleton hungrySingleton = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getSingleton() { return hungrySingleton; } }
这样子有个问题,就是只要这个类被加载了,那么就会创建出唯一实例,也不管用不用...
虽然其实工作中问题不大,但是学习嘛,就要吹毛求疵,我们要懒加载的方式!
4.2 懒汉式
代码如下:
public class LazySingleton { private static LazySingleton lazySingleton = null; private LazySingleton() { } public static LazySingleton getSingleton() { if (null == lazySingleton) { lazySingleton = new LazySingleton(); } return lazySingleton; } }
这种方式也挺好理解的,而且实现了懒加载!只有在调用的时候才创建实例,节省了好大的空间呢!(并不)
但是这种方式仍然是有问题的,那就是著名的你有现在问题两个了
.
如果多个线程同时来请求获取实例,上面这种懒汉式是解决不了的,会提供多个实例,也就违背了单例模式的初衷了(多个线程同时进入判空语句).
4.3 的懒汉优化一下
不就是线程安全吗?把我知道的volatile和synchronized都用上!
public class LazySingleton2 { private static volatile LazySingleton2 lazySingleton = null; private LazySingleton2() { } public static LazySingleton2 getSingleton() { synchronized (LazySingleton2.class) { if (null == lazySingleton) { lazySingleton = new LazySingleton2(); } } return lazySingleton; } }
这种方法看起来没有问题了,用volatilew修饰了唯一实例,保证内存可见性,用synchronized加锁,每次只允许一个线程访问判空语句,这不就解决了上面的问题吗?
是的,杀鸡用牛刀也不一定做的好啊..想想判空语句以及里面的实例化的执行频率,从理想的情况来讲,只有第一次会执行创建实例,剩下的都是返回实例就完事了.
为了这一种情况,每次都加锁,,性能下降太厉害了(其实并不,加了锁我们大部分时间也是够用的).
那再优化一下.
4.4 双重检查锁
代码如下:
public class DoubleCheckSingleton { private static volatile DoubleCheckSingleton singleton = null; private DoubleCheckSingleton() { } public static DoubleCheckSingleton getSingleton() { if (null == singleton) { synchronized (DoubleCheckSingleton.class) { if (null == singleton) { singleton = new DoubleCheckSingleton(); } } } //2 return singleton; } }
这就是传说中的双重检查锁
了,说实话,这个代码看起来我觉得有点难看....
但是其实是比较好使的,大部分的获取实例请求都会直接来到//2
位置,而极少量的为空进行加锁,保证线程安全.
双重检查锁对上一步的优化是:多添加一重判断,过滤掉大部分不需要加锁的操作,同时,加锁后再次进行判断,防止在第一次判断-加锁
期间已经创建了实例.
4.5 静态内部类实现
public class InnerClassSingleton { private static class Holder { private static InnerClassSingleton singleton = new InnerClassSingleton(); } private InnerClassSingleton() { } public static InnerClassSingleton getSingleton() { return Holder.singleton; } }
我们可以把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的:
4.6 枚举写法
上面的所有实现都有一点小问题:
- 序列化与反序列化没有考虑,每次反序列化都能拿到一个新的实例.
- 反射,都可以通过反射强行调用privite的构造方法.
这时候就是枚举类出现的时候了!
public enum EnumSingleton { SINGLETON; }
在《Effective Java》最后推荐了这样一个写法,看起来简直简单的有点不可思议,那么它是怎么保证以上几点的呢?
- 枚举类的初始化过程天然线程安全.即保证了线程安全.
- 对枚举的序列化与反序列禁止了自定义,由JDK实现,不会出现反序列化多个实例的情况.
在 《Effctive Java》中,作者极力推荐枚举实现单例,甚至说了它是单例实现的最好写法.
虽然我还没有应用过枚举实现单例,但是很快我就会将它加进我的代码库里.
总结
在单例实现中,我们需要注意以下三个问题:
- (重要)延迟加载,避免浪费.
- (重要)线程安全,避免多个实例.
- 序列化安全.
完。
ChangeLog
2019-01-31 完成
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
如何在Windows 10 / 8.1 / 8中安装PHP或XAMP
如何在Windows 10 / 8.1 / 8中安装PHP或XAMP 欢迎来到PHP系列简介。在上一篇文章中,我谈到了PHP是什么,它的工作原理。但是PHP不会预装在任何操作系统上,无论是Windows,Linux还是Mac。在本文中,我将向您展示如何在Windows 10 / 8.1 / 8中安装PHP。 正如我在上一篇文章中提到的,PHP没有预装在任何操作系统上。如果您正在学习Web开发,那么您将需要使PHP环境能够运行PHP并在浏览器中查看输出。 PHP和必需的包 网络服务器 PHP是一种开源编程语言,因此您可以下载并安装它。但是能够在浏览器中看到PHP输出就足够了。如果您只是安装PHP并编写一些PHP代码,请在浏览器中打开它,然后代码将不会执行。您将在代码编辑器中输入代码。除了PHP,我们还需要一个Web服务器。现在不要将Web服务器视为具有太字节安装内存的超级计算机。这里的Web服务器是一个简单的程序,它位于您的计算机上并监听HTTP请求,并根据这些请求,Web服务器处理信息并将输出发送到用户浏览器。 所以我们还需要一个Web服务器。 数据库系统 如今,在几乎所有现代Web应...
- 下一篇
想不想在无聊的时候,有个人能陪你聊聊天,python来帮你
# 前言想不想在无聊 寂寞的时候,能有个人陪你聊聊天,是不是很酷,很爽,很想拥有?用python语言,itchar库,图灵机器人就可以直接实现。 代码如下导入需要使用的第三方库 #导入itchar库,用来登录微信,接收并回复微信好友信息 import itchar 获取来自机器人的回复信息在这里,调用图灵机器人库,把我们接收到的微信好友信息发给图灵机器人,再取回机器人回复的信息,回复给好友。 #获取来自机器人api的回复信息 def get_response(msg): apiUrl = '图灵网站API' data = {'key' : '图灵密钥','info':msg,'userid':'本仙女'} r = requests.post(apiUrl,data=data).josn() return r.get('text') 这里要用到图灵机器人的接口,到图灵机器人官网(http://www.tuling123.com) 注册登陆之后,生成一个属于个人的免费接口,免费接口一天只能用1000条,虽然不多,但娱乐一下自己还是OK滴。 创建机器人成功之后会得到apikey,把这串密码放...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果