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

 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 }  

这种方式基于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 }  

这种方式是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已经没有双重检查锁定的问题了。

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

superheizai同学总结的很到位:

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

我很高兴有这样的读者,一起共勉。

Java:单例模式的七种写法[转]的更多相关文章

  1. Java 单例模式的七种写法

    Java 单例模式的七种写法 第一种(懒汉,线程不安全) public class Singleton { private static Singleton instance; private Sin ...

  2. Java设计模式之单例模式(七种写法)

    Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...

  3. 单例模式:Java单例模式的几种写法及它们的优缺点

    总结下Java单例模式的几种写法: 1. 饿汉式 public class Singleton { private static Singleton instance = new Singleton( ...

  4. Android设计模式之单例模式的七种写法

    一 单例模式介绍及它的使用场景 单例模式是应用最广的模式,也是我最先知道的一种设计模式.在深入了解单例模式之前.每当遇到如:getInstance()这样的创建实例的代码时,我都会把它当做一种单例模式 ...

  5. Java:单例模式的七种写法

    第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton ( ...

  6. Java:单例模式的七种写法(转载)

    第一种(懒汉,线程不安全): package Singleton; /** * @echo 2013-10-10 懒汉 线程不安全 */ public class Singleton1 { priva ...

  7. 【JAVA学习】单例模式的七种写法

    尊重版权:http://cantellow.iteye.com/blog/838473 第一种(懒汉.线程不安全): Java代码   public class Singleton { private ...

  8. Java:单例模式的七种写法<转>

    第一种(懒汉,线程不安全):  1 public class Singleton {   2     private static Singleton instance;   3     privat ...

  9. 温故而知新(java实现)单例模式的七种写法

    第一种(懒汉,线程不安全): Java代码 public class Singleton { private static Singleton instance; private Singleton ...

随机推荐

  1. Linux红黑树(一)——数据结构

    摘要 兹博文探讨四个重点:1.简单介绍红黑树:2.红黑树节点数据结构:3.红黑树节点中父节点指针域和自身节点颜色有机结合:4.定义红黑树和操作树节点父节点指针和节点颜色的接口,包括一系列宏和两个函数. ...

  2. CSS常用选择器

    关于CSS常用选择器: 1.ID选择器 关于ID选择器具有唯一性,在文档流中,ID是唯一的,在低版本的浏览器中,允许出现不适唯一ID的情况,而在高版本的浏览器中,出现ID不唯一的情况浏览器会出现的报错 ...

  3. 【GitHub-SwipeMenuListView】针对ListView item的側滑菜单

    项目地址:https://github.com/baoyongzhang/SwipeMenuListView Usage Step 1:import swipemenulistview.jar Ste ...

  4. 重学《C#高级编程》(序)

    小生码农一枚,以前只是看别人写博客,从来没有想过要自己写博文,突然之间“脑抽”想自己也写点什么,遂在博客园开通这个博客. 简单介绍下自己吧,本人90后,父母对我没有大的想法,只是希望我平安成长,多学习 ...

  5. memcached原理全面剖析

    memcached会预先分配内存,memcached分配内存方式称之为allocator, 首先,这里有3个概念: 1 slab 2 page 3 chunk 一般来说一个memcahced进程会预先 ...

  6. Swift - guard关键字(守护)

    guard语句和if语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么.但与if语句不同的是,guard语句只会有一个代码块,不像if语句可以if else多个代码块. 那么guar ...

  7. 设置cas server使用http非安全协议

    目录 1.tomcat添加https安全协议 2.下载cas server端部署到tomcat上 3.CAS服务器深入配置(连接MYSQL) 4.Apache Shiro 集成Cas作为cas cli ...

  8. ubuntu 快捷键和安装知识知识

    本文节选自“The Official Ubuntu Book, 7th Edition.pdf” 快捷键部分直接引用原书中图片. Linux Folders Learning Unity Keyboa ...

  9. JavaScript高级程序设计第20章JSON 笔记 (学习笔记)

    第二十章 JSON 1.Json 可以表示三种类型的值: 1.简单值: 表示数值:5  表示字符串:“hello wrold”注表示字符串时必须使用双引号 2.对象: {“name”:“mi”,”ag ...

  10. 作业:汽车查询--弹窗显示详情,批量删除 php做法(0521)

    作业:显示以下界面: 作业要求: 1.查看详细信息,以弹窗的形式显示,使用ajax2.批量删除 一.主页面: <!DOCTYPE html PUBLIC "-//W3C//DTD XH ...