文档章节

单例模式的七种写法

完美世界
 完美世界
发布于 2014/06/05 17:33
字数 1413
阅读 12
收藏 0
点赞 0
评论 0

第一种(懒汉,线程不安全):


 1 public class Singleton {

2     private static Singleton instance;

3     private Singleton (){}

4     public static Singleton getInstance() {

5     if (instance == null) {

6         instance = new Singleton();

7     }

8     return instance;

9     }

10 }

11

这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。


第二种(懒汉,线程安全):


 1 public class Singleton {

2     private static Singleton instance;

3     private Singleton (){}

4     public static synchronized Singleton getInstance() {

5     if (instance == null) {

6         instance = new Singleton();

7     }

8     return instance;

9     }

10 }

11

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。


第三种(饿汉):


1 public class Singleton {

2     private static Singleton instance = new Singleton();

3     private Singleton (){}

4     public static Singleton getInstance() {

5     return instance;

6     }

7 }

8

这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。


第四种(饿汉,变种):


 1 public class Singleton {

2     private Singleton instance = null;

3     static {

4     instance = new Singleton();

5     }

6     private Singleton (){}

7     public static Singleton getInstance() {

8     return this.instance;

9     }

10 }

11

表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。


第五种(静态内部类):


 1 public class Singleton {

2     private static class SingletonHolder {

3     private static final Singleton INSTANCE = new Singleton();

4     }

5     private Singleton (){}

6     public static final Singleton getInstance() {

7         return SingletonHolder.INSTANCE;

8     }

9 }

10

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。


第六种(枚举):


1 public enum Singleton {

2     INSTANCE;

3     public void whateverMethod() {

4     }

5 }

6

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。


第七种(双重校验锁):


 1 public class Singleton {

2     private volatile static Singleton singleton;

3     private Singleton (){}

4     public static Singleton getSingleton() {

5     if (singleton == null) {

6         synchronized (Singleton.class) {

7         if (singleton == null) {

8             singleton = new Singleton();

9         }

10         }

11     }

12     return singleton;

13     }

14 }

15

这个是第二种方式的升级版,俗称双重检查锁定,详细介绍请查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html


在JDK1.5之后,双重检查锁定才能够正常达到单例效果。


总结


有两个问题需要注意:


1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类  装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。


2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。


对第一个问题修复的办法是:


 1 private static Class getClass(String classname)

2                                          throws ClassNotFoundException {

3       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

4

5       if(classLoader == null)

6          classLoader = Singleton.class.getClassLoader();

7

8       return (classLoader.loadClass(classname));

9    }

10 }

11

对第二个问题修复的办法是:


 1 public class Singleton implements java.io.Serializable {

2    public static Singleton INSTANCE = new Singleton();

3

4    protected Singleton() {

5

6    }

7    private Object readResolve() {

8             return INSTANCE;

9       }

10 }

11

对我来说,我比较喜欢第三种和第五种方式,简单易懂,而且在JVM层实现了线程安全(如果不是多个类加载器环境),一般的情况下,我会使用第三种方式,只有在要明确实现lazy loading效果时才会使用第五种方式,另外,如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例,不过,我一直会保证我的程序是线程安全的,而且我永远不会使用第一种和第二种方式,如果有其他特殊的需求,我可能会使用第七种方式,毕竟,JDK1.5已经没有双重检查锁定的问题了。


================================================================


不过一般来说,第一种不算单例,第四种和第三种就是一种,如果算的话,第五种也可以分开写了。所以说,一般单例都是五种写法。懒汉,恶汉,双重校验锁,枚举和静态内部类。。


© 著作权归作者所有

共有 人打赏支持
完美世界
粉丝 10
博文 151
码字总数 134011
作品 0
西城
高级程序员
(目录)设计模式(可复用面向对象软件的基础)

本系列“设计模式”博客使用Golang语言实现算法。所谓算法是指解决一个问题的步骤,个人觉得不在于语言。小弟只是最近学习Golang,所以顺带熟练一下语法知识,别无它意。 本系列博客主要介绍...

chapin
2015/01/13
0
0
java设计模式-- 单例模式

在很久之前,也就是在大二暑假的时候,那时候看马士兵的视频教程中有提到很多的设计模式。 java的设计模式大致可以分为3大类,23种设计模式。 其中,创建型模式有5种:单例模式、建造者模式、...

爱学习的逃课君
2014/11/27
0
0
【设计模式笔记】(十六)- 代理模式

一、简述 代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。 其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中...

MrTrying
06/24
0
0
为什么我墙裂建议大家使用枚举来实现单例。

关于单例模式,我的博客中有很多文章介绍过。作为23种设计模式中最为常用的设计模式,单例模式并没有想象的那么简单。因为在设计单例的时候要考虑很多问题,比如线程安全问题、序列化对单例的...

06/10
0
0
使用合适的设计模式一步步优化前端代码

作者:晓飞 本文原创,转载请注明作者及出处 --- 在后端语言中,设计模式应用的较为广泛。如Spring中常见的工厂模式、装饰者模式、单例模式、迭代器模式。但是在日常的前端开发中,设计模式使...

iKcamp
2017/10/27
0
0
设计模式(创建型)之单例模式(Singleton Pattern)

转载来自:http://blog.csdn.net/yanbober/article/details/45312675 PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。因为CSDN也支持MarkDown语法了,牛逼啊! 【...

xiaopangzi520
06/26
0
0
小菜学设计模式——设计模式总结之结构型

1、设计模式总结 设计模式总共23个,但是常用的不到10个,下面就把这23个设计模式进行整理归类,具体如下: 1)创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型...

learn_more
2015/07/06
0
0
简单工厂和工厂方法以及抽象工厂模式

工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类 抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类 个人觉得这个区别在于产品,如果产...

王大叔爱编程
2014/09/26
0
0
设计模式梳理(一)

设计模式梳理(一) 总体来说设计模式分为三大类: @案例源码地址:https://gitlab.com/lxqxsyu/DisgnPattern 创建型模式 简单工厂模式 工厂类是整个模式的关键。它包含必要的判断逻辑,能够...

lxq_xsyu
2017/11/02
0
0
设计模式什么的哪有那么神秘 ----第一集 一些吐槽和重构的韵味

一日面试, ...... Hr:你对设计模式熟悉吗? 应聘者A:我了解常用的设计模式,平时经常使用工厂模式和单例模式.而且我也看过一些讲23种设计模式的书.其他不常用的模式用的不是太熟. Hr:你用过外观...

架构梦想
2013/08/04
0
7

没有更多内容

加载失败,请刷新页面

加载更多

下一页

防火墙实例

3、一个包过滤防火墙实例 环境:redhat9 加载了string time等模块 eth0 接外网──ppp0 eth1 接内网──192.168.0.0/24 #!/bin/sh modprobe ipt_MASQUERADE modprobe ip_conntrack_ftp modp...

李超小牛子
2分钟前
0
0
TensorFlow 作用域与操作符的受限范围

variable_scope 影响变量和操作符 name_scope 只影响操作符 with tf.name_scope(""),使用空字符串将作用域返回到顶层 tf.variable_scope("") 相当于添加一个空层 import tensorflow as tf...

阿豪boy
12分钟前
0
0
Java面试基础篇——第六篇:常见Map类的区别

常见的map类有: HashMap, ConcurrentHashMap (Jdk1.8) , LinkedHashMap, TreeMap, Hashtable。 其中我们最常用的莫过于HashMap, 和并发情况下使用的ConcurrentHashMap了,它们的主要区别就在...

developlee的潇洒人生
14分钟前
0
0
崛起于Springboot2.X之前端模版freemaker(23)

1、配置文件 spring: freemarker: allow-request-override: false cache: true check-template-location: true charset: UTF-8 content-type: text/html ......

木九天
30分钟前
1
0
spring-boot:run启动时,指定spring.profiles.active

Maven启动指定Profile通过-P,如mvn spring-boot:run -Ptest,但这是Maven的Profile。 如果要指定spring-boot的spring.profiles.active,则必须使用mvn spring-boot:run -Drun.profiles=test......

夜黑人模糊灬
32分钟前
0
0
大数据分析挖掘技术学习:Python文本分类

引言 文本分类作为自然语言处理任务之一,被广泛应用于解决各种商业领域的问题。文本分类的目的是将 文本/文档 自动地归类为一种或多种预定义的类别。常见的文本分类应用如下: • 理解社交媒...

加米谷大数据
37分钟前
0
0
istio-0.8 指标监控,prometheus,grafana

配置: https://istio.io/docs/tasks/telemetry/metrics-logs/ https://istio.io/docs/tasks/telemetry/tcp-metrics/ envoy拦截请求>上报mixer>对接prometheus>grafana 效果截图: promethe......

xiaomin0322
38分钟前
0
0
公众号推荐

阿里技术 书籍:《不止代码》

courtzjl
41分钟前
0
0
关于改进工作效率

1.给不同的业务线建立需求群,所有的数据需求都在群里面提。 2.对于特别难搞定的事情,到对应的技术哪去做,有问题随时沟通。 3.定期给工作总结形成方法论。 4.学习新的技术,尝试用新的方法...

Avner
48分钟前
0
0
关于thinkphp 框架开启路径重写,无法获取Authorization Header

今天遇到在thinkphp框架中获取不到header头里边的 Authorization ,后来在.htaccess里面加多一项解决,记录下: <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews Rewrite......

殘留回憶
52分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部