前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >漫谈模式之单例模式(通用写法的思考)

漫谈模式之单例模式(通用写法的思考)

作者头像
孟君
发布2023-03-02 22:23:04
3960
发布2023-03-02 22:23:04
举报

在之前的2篇博文漫谈模式之单例模式(多种实现方式的思考)漫谈模式之单例模式(破坏和防护的思考),已经讲解了单例的多种实现方式以及单例在反射、序列化反序列化以及克隆场景下的破坏和防护思考。本文也迎来了漫谈单例模式的最后篇章,如何写一个通用的单例?

在开展讲解之前,先回答一下,为什么要搞一个通用的写法呢

我们知道单例的写法有多种形式,每个人的风格不同。有一个通用的单例框架,可以有效保证风格的一致性。另外,鉴于开发人员的水平不一样,一个经过考验的通用单例模版,可以减少错误的引入。同时,也可以更好地去管理单例的使用。

当然,这并不是强制一定要这么做,使用D.C.L、内部Holder类或者枚举都是可以的。

接下来,我们在双重检查锁、CAS等实现单例的基础,看看通用单例的写法。

双重检查锁-通用写法

接口定义(并不是必须的)

定义一个获取实例接口,如:

图片
图片

抽象类

定义一个抽象的懒加载类。用于指定双重检查锁D.C.L的逻辑,同时指定一个create()方法用于具体子类创建对象使用。

图片
图片

使用示例

继承AbstractLazyInitializer,实现create()方法的内容,如:

图片
图片

然后通过如下方式完成调用即可。

代码语言:javascript
复制
AbstractLazyInitializer<SingletonExam> initializer =                                     new SingletonExam();//获取实例initializer.get();
图片
图片

这样,一个D.C.L的通用写法就弄好了。

CAS-通用写法

CAS我们通过原子类AtomicReference来做。

抽象类

定义一个抽象的懒加载类。维护一个可以原子更新的对象引用(初始值为null), 实现CAS的逻辑,同时指定一个create()方法用于具体子类创建对象使用。

图片
图片

使用示例

继承AbstractAtomicLazyInitializer,实现create()方法的内容,如:

图片
图片

然后通过如下方式完成调用即可。

代码语言:javascript
复制
AbstractAtomicLazyInitializer<SingletonExam> initializer =                                     new SingletonAtomicExam();//获取实例initializer.get();

某次执行的结果如下:

图片
图片

可以看到,多线程在处理CAS操作过程中,可能有多个线程执行到create()去创建了对象,但是,因为只有一个线程更新原子引用成功并返回,其余创建的对象是创建后丢弃的。

那么问题来了, 是否有可能只让create()方法执行一次?

答案是肯定的。

我们可以再增加一个当前类的原子引用,做一道防护在多线程环境下,只有设值成功的,才能去做create()操作。同时,CAS那部分,改成while循环,如下图所示:

CAS抽象类版本2

图片
图片

修改SingletonAtomicExam,继承自AbstractAtomicLazyInitializer2

图片
图片

然后,再执行一下测试代码,其结果如下:

图片
图片

我们可以看到只打印了一行************。也就是说create()方法确实只执行了一次。

上述代码在CAS不成功的时候,打印了do nothing,我们去掉一下。修改后的抽象类如下:

图片
图片

这样,CAS下只创建一次实例的抽象类也弄好了。

上述几种实现的思想,其实是之前看common-lang源码的时候摘记下来的,刚好引用到本文中来。common-lang的截图如下,有兴趣的读者可以去深入看看。

图片
图片

使用Guava的Suppliers.memoize

这是我比较喜欢的一种方式,也曾在项目中使用。

使用示例

编写一个实现Guava Supplier接口的示例

图片
图片

调用方式

代码语言:javascript
复制
Supplier<GuavaSupplierExam> initializer =       Suppliers.memoize(GuavaSupplierExam::new);//获取实例      initializer.get()

测试一下:

图片
图片

Suppliers.memoize实现本质

其实现本质是使用2个变量来完成双重检测锁D.C.L

图片
图片

至此,多种适合单例的懒加载方式就实现就介绍完成了。

整个单例模式的漫谈系列三部曲也完结了。如果读者有其它好的学习内容,也请一起交流学习。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 双重检查锁-通用写法
  • CAS-通用写法
  • 使用Guava的Suppliers.memoize
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档