惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
WordPress大学
WordPress大学
S
Schneier on Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
AWS News Blog
AWS News Blog
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
NISL@THU
NISL@THU
T
Tor Project blog
L
Lohrmann on Cybersecurity
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 聂微东
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
G
GRAHAM CLULEY
博客园_首页
K
Kaspersky official blog
GbyAI
GbyAI
P
Privacy & Cybersecurity Law Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
AI
AI
爱范儿
爱范儿
Cloudbric
Cloudbric
MongoDB | Blog
MongoDB | Blog
Martin Fowler
Martin Fowler
aimingoo的专栏
aimingoo的专栏
I
InfoQ
腾讯CDC
O
OpenAI News
F
Full Disclosure
P
Privacy International News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Webroot Blog
Webroot Blog
Forbes - Security
Forbes - Security
MyScale Blog
MyScale Blog
L
LangChain Blog
H
Help Net Security
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
人人都是产品经理
人人都是产品经理
S
Security @ Cisco Blogs
T
Tenable Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News and Events Feed by Topic
博客园 - 三生石上(FineUI控件)
Attack and Defense Labs
Attack and Defense Labs
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - mshwu

桥接模式(Bridge Pattern) 工厂方法模式 命名空间的规划 Download Manager Intent(二) Intent (一) Fragment (一) 2-1 Managing the Activity Lifecycle java1.5新特性(转) 1-4 Starting Another Activity 1-3 Building a Simple User Interface(创建简单的用户界面) 1-2 Android系统架构基本模式解析 1-1 Building Your First Android Application FTP Send File: Source Code Review(转) 动态操作文件 通用日志管理工具 触发器程序 通过CLP实现library之间的自动复制 (转) OVRDBF的用法
单例模式
mshwu · 2013-09-30 · via 博客园 - mshwu

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

Java代码 收藏代码

  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.     } 

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

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

Java代码 收藏代码

  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.     } 

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

第三种(饿汉):

Java代码 收藏代码

  1. public class Singleton { 
  2. private static Singleton instance = new Singleton(); 
  3. private Singleton (){} 
  4. public static Singleton getInstance() { 
  5. return instance; 
  6.     } 

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

第四种(饿汉,变种):

Java代码 收藏代码

  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.     } 

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

第五种(静态内部类):

Java代码 收藏代码

  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.     } 

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

第六种(枚举):

Java代码 收藏代码

  1. public enum Singleton { 
  2.     INSTANCE; 
  3. public void whateverMethod() { 
  4.     } 

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

第七种(双重校验锁):

Java代码 收藏代码

  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.     } 

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

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

总结

有两个问题需要注意:

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

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

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

Java代码 收藏代码

  1. private static Class getClass(String classname)     
  2. throws ClassNotFoundException {    
  3.       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();    
  4. if(classLoader == null)    
  5.          classLoader = Singleton.class.getClassLoader();    
  6. return (classLoader.loadClass(classname));    
  7.    }    

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

Java代码 收藏代码

  1. public class Singleton implements java.io.Serializable {    
  2. public static Singleton INSTANCE = new Singleton();    
  3. protected Singleton() {    
  4.    }    
  5. private Object readResolve() {    
  6. return INSTANCE;    
  7.       }   
  8. }  

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

转载出处:http://cantellow.iteye.com/blog/838473